diff options
16 files changed, 7781 insertions, 0 deletions
diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp new file mode 100644 index 0000000000000..711d324d8aa7c --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp @@ -0,0 +1,573 @@ +//===-- AppleObjCClassDescriptorV2.cpp -----------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleObjCClassDescriptorV2.h" + +#include "lldb/Core/Log.h" +#include "lldb/Expression/FunctionCaller.h" + +using namespace lldb; +using namespace lldb_private; + +bool +ClassDescriptorV2::Read_objc_class (Process* process, std::unique_ptr<objc_class_t> &objc_class) const +{ +    objc_class.reset(new objc_class_t); +     +    bool ret = objc_class->Read (process, m_objc_class_ptr); +     +    if (!ret) +        objc_class.reset(); +     +    return ret; +} + +bool +ClassDescriptorV2::objc_class_t::Read(Process *process, lldb::addr_t addr) +{ +    size_t ptr_size = process->GetAddressByteSize(); +     +    size_t objc_class_size = ptr_size   // uintptr_t isa; +    + ptr_size   // Class superclass; +    + ptr_size   // void *cache; +    + ptr_size   // IMP *vtable; +    + ptr_size;  // uintptr_t data_NEVER_USE; +     +    DataBufferHeap objc_class_buf (objc_class_size, '\0'); +    Error error; +     +    process->ReadMemory(addr, objc_class_buf.GetBytes(), objc_class_size, error); +    if (error.Fail()) +    { +        return false; +    } +     +    DataExtractor extractor(objc_class_buf.GetBytes(), objc_class_size, process->GetByteOrder(), process->GetAddressByteSize()); +     +    lldb::offset_t cursor = 0; +     +    m_isa           = extractor.GetAddress_unchecked(&cursor);   // uintptr_t isa; +    m_superclass    = extractor.GetAddress_unchecked(&cursor);   // Class superclass; +    m_cache_ptr     = extractor.GetAddress_unchecked(&cursor);   // void *cache; +    m_vtable_ptr    = extractor.GetAddress_unchecked(&cursor);   // IMP *vtable; +    lldb::addr_t data_NEVER_USE = extractor.GetAddress_unchecked(&cursor);   // uintptr_t data_NEVER_USE; +     +    m_flags         = (uint8_t)(data_NEVER_USE & (lldb::addr_t)3); +    m_data_ptr      = data_NEVER_USE & ~(lldb::addr_t)3; +     +    return true; +} + +bool +ClassDescriptorV2::class_rw_t::Read(Process *process, lldb::addr_t addr) +{ +    size_t ptr_size = process->GetAddressByteSize(); +     +    size_t size = sizeof(uint32_t)  // uint32_t flags; +    + sizeof(uint32_t)  // uint32_t version; +    + ptr_size          // const class_ro_t *ro; +    + ptr_size          // union { method_list_t **method_lists; method_list_t *method_list; }; +    + ptr_size          // struct chained_property_list *properties; +    + ptr_size          // const protocol_list_t **protocols; +    + ptr_size          // Class firstSubclass; +    + ptr_size;         // Class nextSiblingClass; +     +    DataBufferHeap buffer (size, '\0'); +    Error error; +     +    process->ReadMemory(addr, buffer.GetBytes(), size, error); +    if (error.Fail()) +    { +        return false; +    } +     +    DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), process->GetAddressByteSize()); +     +    lldb::offset_t cursor = 0; +     +    m_flags             = extractor.GetU32_unchecked(&cursor); +    m_version           = extractor.GetU32_unchecked(&cursor); +    m_ro_ptr            = extractor.GetAddress_unchecked(&cursor); +    m_method_list_ptr   = extractor.GetAddress_unchecked(&cursor); +    m_properties_ptr    = extractor.GetAddress_unchecked(&cursor); +    m_firstSubclass     = extractor.GetAddress_unchecked(&cursor); +    m_nextSiblingClass  = extractor.GetAddress_unchecked(&cursor); +     +    return true; +} + +bool +ClassDescriptorV2::class_ro_t::Read(Process *process, lldb::addr_t addr) +{ +    size_t ptr_size = process->GetAddressByteSize(); +     +    size_t size = sizeof(uint32_t)             // uint32_t flags; +    + sizeof(uint32_t)                         // uint32_t instanceStart; +    + sizeof(uint32_t)                         // uint32_t instanceSize; +    + (ptr_size == 8 ? sizeof(uint32_t) : 0)   // uint32_t reserved; // __LP64__ only +    + ptr_size                                 // const uint8_t *ivarLayout; +    + ptr_size                                 // const char *name; +    + ptr_size                                 // const method_list_t *baseMethods; +    + ptr_size                                 // const protocol_list_t *baseProtocols; +    + ptr_size                                 // const ivar_list_t *ivars; +    + ptr_size                                 // const uint8_t *weakIvarLayout; +    + ptr_size;                                // const property_list_t *baseProperties; +     +    DataBufferHeap buffer (size, '\0'); +    Error error; +     +    process->ReadMemory(addr, buffer.GetBytes(), size, error); +    if (error.Fail()) +    { +        return false; +    } +     +    DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), process->GetAddressByteSize()); +     +    lldb::offset_t cursor = 0; +     +    m_flags             = extractor.GetU32_unchecked(&cursor); +    m_instanceStart     = extractor.GetU32_unchecked(&cursor); +    m_instanceSize      = extractor.GetU32_unchecked(&cursor); +    if (ptr_size == 8) +        m_reserved      = extractor.GetU32_unchecked(&cursor); +    else +        m_reserved      = 0; +    m_ivarLayout_ptr     = extractor.GetAddress_unchecked(&cursor); +    m_name_ptr           = extractor.GetAddress_unchecked(&cursor); +    m_baseMethods_ptr    = extractor.GetAddress_unchecked(&cursor); +    m_baseProtocols_ptr  = extractor.GetAddress_unchecked(&cursor); +    m_ivars_ptr          = extractor.GetAddress_unchecked(&cursor); +    m_weakIvarLayout_ptr = extractor.GetAddress_unchecked(&cursor); +    m_baseProperties_ptr = extractor.GetAddress_unchecked(&cursor); +     +    DataBufferHeap name_buf(1024, '\0'); +     +    process->ReadCStringFromMemory(m_name_ptr, (char*)name_buf.GetBytes(), name_buf.GetByteSize(), error); +     +    if (error.Fail()) +    { +        return false; +    } +     +    m_name.assign((char*)name_buf.GetBytes()); +     +    return true; +} + +bool +ClassDescriptorV2::Read_class_row (Process* process, const objc_class_t &objc_class, std::unique_ptr<class_ro_t> &class_ro, std::unique_ptr<class_rw_t> &class_rw) const +{ +    class_ro.reset(); +    class_rw.reset(); +     +    Error error; +    uint32_t class_row_t_flags = process->ReadUnsignedIntegerFromMemory(objc_class.m_data_ptr, sizeof(uint32_t), 0, error); +    if (!error.Success()) +        return false; +     +    if (class_row_t_flags & RW_REALIZED) +    { +        class_rw.reset(new class_rw_t); +         +        if (!class_rw->Read(process, objc_class.m_data_ptr)) +        { +            class_rw.reset(); +            return false; +        } +         +        class_ro.reset(new class_ro_t); +         +        if (!class_ro->Read(process, class_rw->m_ro_ptr)) +        { +            class_rw.reset(); +            class_ro.reset(); +            return false; +        } +    } +    else +    { +        class_ro.reset(new class_ro_t); +         +        if (!class_ro->Read(process, objc_class.m_data_ptr)) +        { +            class_ro.reset(); +            return false; +        } +    } +     +    return true; +} + +bool +ClassDescriptorV2::method_list_t::Read(Process *process, lldb::addr_t addr) +{ +    size_t size = sizeof(uint32_t)  // uint32_t entsize_NEVER_USE; +    + sizeof(uint32_t); // uint32_t count; +     +    DataBufferHeap buffer (size, '\0'); +    Error error; +     +    process->ReadMemory(addr, buffer.GetBytes(), size, error); +    if (error.Fail()) +    { +        return false; +    } +     +    DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), process->GetAddressByteSize()); +     +    lldb::offset_t cursor = 0; +     +    m_entsize   = extractor.GetU32_unchecked(&cursor) & ~(uint32_t)3; +    m_count     = extractor.GetU32_unchecked(&cursor); +    m_first_ptr  = addr + cursor; +     +    return true; +} + +bool +ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr) +{ +    size_t size = GetSize(process); +     +    DataBufferHeap buffer (size, '\0'); +    Error error; +     +    process->ReadMemory(addr, buffer.GetBytes(), size, error); +    if (error.Fail()) +    { +        return false; +    } +     +    DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), process->GetAddressByteSize()); +     +    lldb::offset_t cursor = 0; +     +    m_name_ptr   = extractor.GetAddress_unchecked(&cursor); +    m_types_ptr  = extractor.GetAddress_unchecked(&cursor); +    m_imp_ptr    = extractor.GetAddress_unchecked(&cursor); +     +    process->ReadCStringFromMemory(m_name_ptr, m_name, error); +    if (error.Fail()) +    { +        return false; +    } +     +    process->ReadCStringFromMemory(m_types_ptr, m_types, error); +    if (error.Fail()) +    { +        return false; +    } +     +    return true; +} + +bool +ClassDescriptorV2::ivar_list_t::Read(Process *process, lldb::addr_t addr) +{ +    size_t size = sizeof(uint32_t)  // uint32_t entsize; +    + sizeof(uint32_t); // uint32_t count; +     +    DataBufferHeap buffer (size, '\0'); +    Error error; +     +    process->ReadMemory(addr, buffer.GetBytes(), size, error); +    if (error.Fail()) +    { +        return false; +    } +     +    DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), process->GetAddressByteSize()); +     +    lldb::offset_t cursor = 0; +     +    m_entsize   = extractor.GetU32_unchecked(&cursor); +    m_count     = extractor.GetU32_unchecked(&cursor); +    m_first_ptr = addr + cursor; +     +    return true; +} + +bool +ClassDescriptorV2::ivar_t::Read(Process *process, lldb::addr_t addr) +{ +    size_t size = GetSize(process); +     +    DataBufferHeap buffer (size, '\0'); +    Error error; +     +    process->ReadMemory(addr, buffer.GetBytes(), size, error); +    if (error.Fail()) +    { +        return false; +    } +     +    DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), process->GetAddressByteSize()); +     +    lldb::offset_t cursor = 0; +     +    m_offset_ptr = extractor.GetAddress_unchecked(&cursor); +    m_name_ptr   = extractor.GetAddress_unchecked(&cursor); +    m_type_ptr   = extractor.GetAddress_unchecked(&cursor); +    m_alignment  = extractor.GetU32_unchecked(&cursor); +    m_size       = extractor.GetU32_unchecked(&cursor); +     +    process->ReadCStringFromMemory(m_name_ptr, m_name, error); +    if (error.Fail()) +    { +        return false; +    } +     +    process->ReadCStringFromMemory(m_type_ptr, m_type, error); +    if (error.Fail()) +    { +        return false; +    } +     +    return true; +} + +bool +ClassDescriptorV2::Describe (std::function <void (ObjCLanguageRuntime::ObjCISA)> const &superclass_func, +                             std::function <bool (const char *, const char *)> const &instance_method_func, +                             std::function <bool (const char *, const char *)> const &class_method_func, +                             std::function <bool (const char *, const char *, lldb::addr_t, uint64_t)> const &ivar_func) const +{ +    lldb_private::Process *process = m_runtime.GetProcess(); +     +    std::unique_ptr<objc_class_t> objc_class; +    std::unique_ptr<class_ro_t> class_ro; +    std::unique_ptr<class_rw_t> class_rw; +     +    if (!Read_objc_class(process, objc_class)) +        return 0; +    if (!Read_class_row(process, *objc_class, class_ro, class_rw)) +        return 0; +     +    static ConstString NSObject_name("NSObject"); +     +    if (m_name != NSObject_name && superclass_func) +        superclass_func(objc_class->m_superclass); +     +    if (instance_method_func) +    { +        std::unique_ptr<method_list_t> base_method_list; +         +        base_method_list.reset(new method_list_t); +        if (!base_method_list->Read(process, class_ro->m_baseMethods_ptr)) +            return false; +         +        if (base_method_list->m_entsize != method_t::GetSize(process)) +            return false; +         +        std::unique_ptr<method_t> method; +        method.reset(new method_t); +         +        for (uint32_t i = 0, e = base_method_list->m_count; i < e; ++i) +        { +            method->Read(process, base_method_list->m_first_ptr + (i * base_method_list->m_entsize)); +             +            if (instance_method_func(method->m_name.c_str(), method->m_types.c_str())) +                break; +        } +    } +     +    if (class_method_func) +    { +        AppleObjCRuntime::ClassDescriptorSP metaclass(GetMetaclass()); +         +        // We don't care about the metaclass's superclass, or its class methods.  Its instance methods are +        // our class methods. +         +        if (metaclass) { +            metaclass->Describe(std::function <void (ObjCLanguageRuntime::ObjCISA)> (nullptr), +                                class_method_func, +                                std::function <bool (const char *, const char *)> (nullptr), +                                std::function <bool (const char *, const char *, lldb::addr_t, uint64_t)> (nullptr)); +        } +    } +     +    if (ivar_func) +    { +        if (class_ro->m_ivars_ptr != 0) +        {             +            ivar_list_t ivar_list; +            if (!ivar_list.Read(process, class_ro->m_ivars_ptr)) +                return false; +             +            if (ivar_list.m_entsize != ivar_t::GetSize(process)) +                return false; +             +            ivar_t ivar; +             +            for (uint32_t i = 0, e = ivar_list.m_count; i < e; ++i) +            { +                ivar.Read(process, ivar_list.m_first_ptr + (i * ivar_list.m_entsize)); +                 +                if (ivar_func(ivar.m_name.c_str(), ivar.m_type.c_str(), ivar.m_offset_ptr, ivar.m_size)) +                    break; +            } +        } +    } +     +    return true; +} + +ConstString +ClassDescriptorV2::GetClassName () +{ +    if (!m_name) +    { +        lldb_private::Process *process = m_runtime.GetProcess(); +         +        if (process) +        { +            std::unique_ptr<objc_class_t> objc_class; +            std::unique_ptr<class_ro_t> class_ro; +            std::unique_ptr<class_rw_t> class_rw; +             +            if (!Read_objc_class(process, objc_class)) +                return m_name; +            if (!Read_class_row(process, *objc_class, class_ro, class_rw)) +                return m_name; +             +            m_name = ConstString(class_ro->m_name.c_str()); +        } +    } +    return m_name; +} + +ObjCLanguageRuntime::ClassDescriptorSP +ClassDescriptorV2::GetSuperclass () +{ +    lldb_private::Process *process = m_runtime.GetProcess(); +     +    if (!process) +        return ObjCLanguageRuntime::ClassDescriptorSP(); +     +    std::unique_ptr<objc_class_t> objc_class; +     +    if (!Read_objc_class(process, objc_class)) +        return ObjCLanguageRuntime::ClassDescriptorSP(); +     +    return m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA(objc_class->m_superclass); +} + +ObjCLanguageRuntime::ClassDescriptorSP +ClassDescriptorV2::GetMetaclass () const +{ +    lldb_private::Process *process = m_runtime.GetProcess(); +     +    if (!process) +        return ObjCLanguageRuntime::ClassDescriptorSP(); +     +    std::unique_ptr<objc_class_t> objc_class; +     +    if (!Read_objc_class(process, objc_class)) +        return ObjCLanguageRuntime::ClassDescriptorSP(); +     +    lldb::addr_t candidate_isa = m_runtime.GetPointerISA(objc_class->m_isa); +     +    return ObjCLanguageRuntime::ClassDescriptorSP(new ClassDescriptorV2(m_runtime, candidate_isa, nullptr)); +} + +uint64_t +ClassDescriptorV2::GetInstanceSize () +{ +    lldb_private::Process *process = m_runtime.GetProcess(); +     +    if (process) +    { +        std::unique_ptr<objc_class_t> objc_class; +        std::unique_ptr<class_ro_t> class_ro; +        std::unique_ptr<class_rw_t> class_rw; +         +        if (!Read_objc_class(process, objc_class)) +            return 0; +        if (!Read_class_row(process, *objc_class, class_ro, class_rw)) +            return 0; +         +        return class_ro->m_instanceSize; +    } +     +    return 0; +} + +ClassDescriptorV2::iVarsStorage::iVarsStorage (): +m_filled(false), +m_ivars(), +m_mutex(Mutex::eMutexTypeRecursive) +{} + +size_t +ClassDescriptorV2::iVarsStorage::size () +{ +    return m_ivars.size(); +} + +ClassDescriptorV2::iVarDescriptor& +ClassDescriptorV2::iVarsStorage::operator[] (size_t idx) +{ +    return m_ivars[idx]; +} + +void +ClassDescriptorV2::iVarsStorage::fill (AppleObjCRuntimeV2& runtime, ClassDescriptorV2& descriptor) +{ +    if (m_filled) +        return; +    Mutex::Locker lock(m_mutex); +    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES | LIBLLDB_LOG_VERBOSE)); +    if (log) +        log->Printf("[ClassDescriptorV2::iVarsStorage::fill] class_name = %s", descriptor.GetClassName().AsCString("<unknown")); +    m_filled = true; +    ObjCLanguageRuntime::EncodingToTypeSP encoding_to_type_sp(runtime.GetEncodingToType()); +    Process* process(runtime.GetProcess()); +    if (!encoding_to_type_sp) +        return; +    descriptor.Describe(nullptr, +                        nullptr, +                        nullptr, +                        [this,process,encoding_to_type_sp,log](const char * name, const char * type, lldb::addr_t offset_ptr, uint64_t size) -> bool { +                 const bool for_expression = false; +                 const bool stop_loop = false; +                 if (log) +                     log->Printf("[ClassDescriptorV2::iVarsStorage::fill] name = %s, encoding = %s, offset_ptr = %" PRIx64 ", size = %" PRIu64, +                                 name,type,offset_ptr,size); +                 CompilerType ivar_type = encoding_to_type_sp->RealizeType(type, for_expression); +                 if (ivar_type) +                 { +                     if (log) +                         log->Printf("[ClassDescriptorV2::iVarsStorage::fill] name = %s, encoding = %s, offset_ptr = %" PRIx64 ", size = %" PRIu64 " , type_size = %" PRIu64, +                                     name,type,offset_ptr,size,ivar_type.GetByteSize(nullptr)); +                     Scalar offset_scalar; +                     Error error; +                     const int offset_ptr_size = 4; +                     const bool is_signed = false; +                     size_t read = process->ReadScalarIntegerFromMemory(offset_ptr, offset_ptr_size, is_signed, offset_scalar, error); +                     if (error.Success() && 4 == read) +                     { +                         if (log) +                             log->Printf("[ClassDescriptorV2::iVarsStorage::fill] offset_ptr = %" PRIx64 " --> %" PRIu32, +                                         offset_ptr, offset_scalar.SInt()); +                         m_ivars.push_back({ ConstString(name), ivar_type, size, offset_scalar.SInt() }); +                     } +                     else if (log) +                         log->Printf("[ClassDescriptorV2::iVarsStorage::fill] offset_ptr = %" PRIx64 " --> read fail, read = %zu", +                                     offset_ptr, read); +                 } +                 return stop_loop; +             }); +} + +void +ClassDescriptorV2::GetIVarInformation () +{ +    m_ivars_storage.fill(m_runtime, *this); +} diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h new file mode 100644 index 0000000000000..18591ecd6e936 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h @@ -0,0 +1,411 @@ +//===-- AppleObjCClassDescriptorV2.h ----------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AppleObjCClassDescriptorV2_h_ +#define liblldb_AppleObjCClassDescriptorV2_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "AppleObjCRuntimeV2.h" + +namespace lldb_private { + +class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor +{ +public: +    friend class lldb_private::AppleObjCRuntimeV2; +     +    ~ClassDescriptorV2() override = default; + +    ConstString +    GetClassName() override; +     +    ObjCLanguageRuntime::ClassDescriptorSP +    GetSuperclass() override; +     +    ObjCLanguageRuntime::ClassDescriptorSP +    GetMetaclass() const override; +     +    bool +    IsValid() override +    { +        return true;    // any Objective-C v2 runtime class descriptor we vend is valid +    } +     +    // a custom descriptor is used for tagged pointers +    bool +    GetTaggedPointerInfo(uint64_t* info_bits = nullptr, +                         uint64_t* value_bits = nullptr, +                         uint64_t* payload = nullptr) override +    { +        return false; +    } +     +    uint64_t +    GetInstanceSize() override; +     +    ObjCLanguageRuntime::ObjCISA +    GetISA() override +    { +        return m_objc_class_ptr; +    } +     +    bool +    Describe(std::function <void (ObjCLanguageRuntime::ObjCISA)> const &superclass_func, +             std::function <bool (const char *, const char *)> const &instance_method_func, +             std::function <bool (const char *, const char *)> const &class_method_func, +             std::function <bool (const char *, const char *, +             lldb::addr_t, uint64_t)> const &ivar_func) const override; + +    size_t +    GetNumIVars() override +    { +        GetIVarInformation(); +        return m_ivars_storage.size(); +    } +     +    iVarDescriptor +    GetIVarAtIndex(size_t idx) override +    { +        if (idx >= GetNumIVars()) +            return iVarDescriptor(); +        return m_ivars_storage[idx]; +    } +     +protected: +    void +    GetIVarInformation (); +     +private: +    static const uint32_t RW_REALIZED = (1 << 31); +     +    struct objc_class_t { +        ObjCLanguageRuntime::ObjCISA    m_isa;              // The class's metaclass. +        ObjCLanguageRuntime::ObjCISA    m_superclass; +        lldb::addr_t                    m_cache_ptr; +        lldb::addr_t                    m_vtable_ptr; +        lldb::addr_t                    m_data_ptr; +        uint8_t                         m_flags; +         +        objc_class_t () : +        m_isa (0), +        m_superclass (0), +        m_cache_ptr (0), +        m_vtable_ptr (0), +        m_data_ptr (0), +        m_flags (0) +        { +        } +         +        void +        Clear() +        { +            m_isa = 0; +            m_superclass = 0; +            m_cache_ptr = 0; +            m_vtable_ptr = 0; +            m_data_ptr = 0; +            m_flags = 0; +        } +         +        bool +        Read(Process *process, lldb::addr_t addr); +    }; +     +    struct class_ro_t { +        uint32_t                        m_flags; +        uint32_t                        m_instanceStart; +        uint32_t                        m_instanceSize; +        uint32_t                        m_reserved; +         +        lldb::addr_t                    m_ivarLayout_ptr; +        lldb::addr_t                    m_name_ptr; +        lldb::addr_t                    m_baseMethods_ptr; +        lldb::addr_t                    m_baseProtocols_ptr; +        lldb::addr_t                    m_ivars_ptr; +         +        lldb::addr_t                    m_weakIvarLayout_ptr; +        lldb::addr_t                    m_baseProperties_ptr; +         +        std::string                     m_name; +         +        bool +        Read(Process *process, lldb::addr_t addr); +    }; +     +    struct class_rw_t { +        uint32_t                        m_flags; +        uint32_t                        m_version; +         +        lldb::addr_t                    m_ro_ptr; +        union { +            lldb::addr_t                m_method_list_ptr; +            lldb::addr_t                m_method_lists_ptr; +        }; +        lldb::addr_t                    m_properties_ptr; +        lldb::addr_t                    m_protocols_ptr; +         +        ObjCLanguageRuntime::ObjCISA    m_firstSubclass; +        ObjCLanguageRuntime::ObjCISA    m_nextSiblingClass; +         +        bool +        Read(Process *process, lldb::addr_t addr); +    }; +     +    struct method_list_t +    { +        uint32_t        m_entsize; +        uint32_t        m_count; +        lldb::addr_t    m_first_ptr; +         +        bool +        Read(Process *process, lldb::addr_t addr); +    }; +     +    struct method_t +    { +        lldb::addr_t    m_name_ptr; +        lldb::addr_t    m_types_ptr; +        lldb::addr_t    m_imp_ptr; +         +        std::string     m_name; +        std::string     m_types; +         +        static size_t GetSize(Process *process) +        { +            size_t ptr_size = process->GetAddressByteSize(); +             +            return ptr_size     // SEL name; +            + ptr_size   // const char *types; +            + ptr_size;  // IMP imp; +        } +         +        bool +        Read(Process *process, lldb::addr_t addr); +    }; +     +    struct ivar_list_t +    { +        uint32_t        m_entsize; +        uint32_t        m_count; +        lldb::addr_t    m_first_ptr; +         +        bool Read(Process *process, lldb::addr_t addr); +    }; +     +    struct ivar_t +    { +        lldb::addr_t    m_offset_ptr; +        lldb::addr_t    m_name_ptr; +        lldb::addr_t    m_type_ptr; +        uint32_t        m_alignment; +        uint32_t        m_size; +         +        std::string     m_name; +        std::string     m_type; +         +        static size_t GetSize(Process *process) +        { +            size_t ptr_size = process->GetAddressByteSize(); +             +            return ptr_size             // uintptr_t *offset; +            + ptr_size             // const char *name; +            + ptr_size             // const char *type; +            + sizeof(uint32_t)     // uint32_t alignment; +            + sizeof(uint32_t);    // uint32_t size; +        } +         +        bool +        Read(Process *process, lldb::addr_t addr); +    }; +     +    class iVarsStorage +    { +    public: +        iVarsStorage (); +         +        size_t +        size (); +         +        iVarDescriptor& +        operator[] (size_t idx); +         +        void +        fill (AppleObjCRuntimeV2& runtime, ClassDescriptorV2& descriptor); +         +    private: +        bool m_filled; +        std::vector<iVarDescriptor> m_ivars; +        Mutex m_mutex; +    }; +     +    // The constructor should only be invoked by the runtime as it builds its caches +    // or populates them.  A ClassDescriptorV2 should only ever exist in a cache. +    ClassDescriptorV2(AppleObjCRuntimeV2 &runtime, ObjCLanguageRuntime::ObjCISA isa, const char *name) : +        m_runtime (runtime), +        m_objc_class_ptr (isa), +        m_name (name), +        m_ivars_storage() +    { +    } +     +    bool +    Read_objc_class (Process* process, std::unique_ptr<objc_class_t> &objc_class) const; +     +    bool +    Read_class_row (Process* process, const objc_class_t &objc_class, std::unique_ptr<class_ro_t> &class_ro, std::unique_ptr<class_rw_t> &class_rw) const; +     +    AppleObjCRuntimeV2 &m_runtime;          // The runtime, so we can read information lazily. +    lldb::addr_t        m_objc_class_ptr;   // The address of the objc_class_t.  (I.e., objects of this class type have this as their ISA) +    ConstString         m_name;             // May be NULL +    iVarsStorage        m_ivars_storage; +}; + +// tagged pointer descriptor +class ClassDescriptorV2Tagged : public ObjCLanguageRuntime::ClassDescriptor +{ +public: +    ClassDescriptorV2Tagged (ConstString class_name, +                             uint64_t payload) +    { +        m_name = class_name; +        if (!m_name) +        { +            m_valid = false; +            return; +        } +        m_valid = true; +        m_payload = payload; +        m_info_bits = (m_payload & 0xF0ULL) >> 4; +        m_value_bits = (m_payload & ~0x0000000000000000FFULL) >> 8; +    } +     +    ClassDescriptorV2Tagged (ObjCLanguageRuntime::ClassDescriptorSP actual_class_sp, +                             uint64_t payload) +    { +        if (!actual_class_sp) +        { +            m_valid = false; +            return; +        } +        m_name = actual_class_sp->GetClassName(); +        if (!m_name) +        { +            m_valid = false; +            return; +        } +        m_valid = true; +        m_payload = payload; +        m_info_bits = (m_payload & 0x0FULL); +        m_value_bits = (m_payload & ~0x0FULL) >> 4; +    } +     +    ~ClassDescriptorV2Tagged() override = default; + +    ConstString +    GetClassName() override +    { +        return m_name; +    } +     +    ObjCLanguageRuntime::ClassDescriptorSP +    GetSuperclass() override +    { +        // tagged pointers can represent a class that has a superclass, but since that information is not +        // stored in the object itself, we would have to query the runtime to discover the hierarchy +        // for the time being, we skip this step in the interest of static discovery +        return ObjCLanguageRuntime::ClassDescriptorSP(); +    } +     +    ObjCLanguageRuntime::ClassDescriptorSP +    GetMetaclass() const override +    { +        return ObjCLanguageRuntime::ClassDescriptorSP(); +    } +     +    bool +    IsValid() override +    { +        return m_valid; +    } +     +    bool +    IsKVO() override +    { +        return false; // tagged pointers are not KVO'ed +    } +     +    bool +    IsCFType() override +    { +        return false; // tagged pointers are not CF objects +    } +     +    bool +    GetTaggedPointerInfo(uint64_t* info_bits = nullptr, +                         uint64_t* value_bits = nullptr, +                         uint64_t* payload = nullptr) override +    { +        if (info_bits) +            *info_bits = GetInfoBits(); +        if (value_bits) +            *value_bits = GetValueBits(); +        if (payload) +            *payload = GetPayload(); +        return true; +    } +     +    uint64_t +    GetInstanceSize() override +    { +        return (IsValid() ? m_pointer_size : 0); +    } +     +    ObjCLanguageRuntime::ObjCISA +    GetISA() override +    { +        return 0; // tagged pointers have no ISA +    } +     +    // these calls are not part of any formal tagged pointers specification +    virtual uint64_t +    GetValueBits () +    { +        return (IsValid() ? m_value_bits : 0); +    } +     +    virtual uint64_t +    GetInfoBits () +    { +        return (IsValid() ? m_info_bits : 0); +    } +     +    virtual uint64_t +    GetPayload () +    { +        return (IsValid() ? m_payload : 0); +    } + +private: +    ConstString m_name; +    uint8_t m_pointer_size; +    bool m_valid; +    uint64_t m_info_bits; +    uint64_t m_value_bits; +    uint64_t m_payload; +}; +     +} // namespace lldb_private + +#endif // liblldb_AppleObjCClassDescriptorV2_h_ diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp new file mode 100644 index 0000000000000..cd6ece297ed13 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp @@ -0,0 +1,665 @@ +//===-- AppleObjCDeclVendor.cpp ---------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleObjCDeclVendor.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "Plugins/ExpressionParser/Clang/ASTDumper.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclObjC.h" + +using namespace lldb_private; + +class lldb_private::AppleObjCExternalASTSource : public ClangExternalASTSourceCommon +{ +public: +    AppleObjCExternalASTSource (AppleObjCDeclVendor &decl_vendor) : +        m_decl_vendor(decl_vendor) +    { +    } + +    bool +    FindExternalVisibleDeclsByName(const clang::DeclContext *decl_ctx, clang::DeclarationName name) override +    { +        static unsigned int invocation_id = 0; +        unsigned int current_id = invocation_id++; + +        Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));  // FIXME - a more appropriate log channel? + +        if (log) +        { +            log->Printf("AppleObjCExternalASTSource::FindExternalVisibleDeclsByName[%u] on (ASTContext*)%p Looking for %s in (%sDecl*)%p", +                        current_id, +                        static_cast<void*>(&decl_ctx->getParentASTContext()), +                        name.getAsString().c_str(), decl_ctx->getDeclKindName(), +                        static_cast<const void*>(decl_ctx)); +        } + +        do +        { +            const clang::ObjCInterfaceDecl *interface_decl = llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl_ctx); + +            if (!interface_decl) +                break; + +            clang::ObjCInterfaceDecl *non_const_interface_decl = const_cast<clang::ObjCInterfaceDecl*>(interface_decl); + +            if (!m_decl_vendor.FinishDecl(non_const_interface_decl)) +                break; + +            clang::DeclContext::lookup_result result = non_const_interface_decl->lookup(name); + +            return (result.size() != 0); +        } +        while(0); + +        SetNoExternalVisibleDeclsForName(decl_ctx, name); +        return false; +    } + +    void +    CompleteType(clang::TagDecl *tag_decl) override +    { +        static unsigned int invocation_id = 0; +        unsigned int current_id = invocation_id++; + +        Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));  // FIXME - a more appropriate log channel? + +        if (log) +        { +            log->Printf("AppleObjCExternalASTSource::CompleteType[%u] on (ASTContext*)%p Completing (TagDecl*)%p named %s", +                        current_id, +                        static_cast<void*>(&tag_decl->getASTContext()), +                        static_cast<void*>(tag_decl), +                        tag_decl->getName().str().c_str()); + +            log->Printf("  AOEAS::CT[%u] Before:", current_id); +            ASTDumper dumper((clang::Decl*)tag_decl); +            dumper.ToLog(log, "    [CT] "); +        } + +        if (log) +        { +            log->Printf("  AOEAS::CT[%u] After:", current_id); +            ASTDumper dumper((clang::Decl*)tag_decl); +            dumper.ToLog(log, "    [CT] "); +        } +        return; +    } + +    void +    CompleteType(clang::ObjCInterfaceDecl *interface_decl) override +    { +        static unsigned int invocation_id = 0; +        unsigned int current_id = invocation_id++; + +        Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));  // FIXME - a more appropriate log channel? + +        if (log) +        { +            log->Printf("AppleObjCExternalASTSource::CompleteType[%u] on (ASTContext*)%p Completing (ObjCInterfaceDecl*)%p named %s", +                        current_id, +                        static_cast<void*>(&interface_decl->getASTContext()), +                        static_cast<void*>(interface_decl), +                        interface_decl->getName().str().c_str()); + +            log->Printf("  AOEAS::CT[%u] Before:", current_id); +            ASTDumper dumper((clang::Decl*)interface_decl); +            dumper.ToLog(log, "    [CT] "); +        } + +        m_decl_vendor.FinishDecl(interface_decl); + +        if (log) +        { +            log->Printf("  [CT] After:"); +            ASTDumper dumper((clang::Decl*)interface_decl); +            dumper.ToLog(log, "    [CT] "); +        } +        return; +    } + +    bool +    layoutRecordType(const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, +                     llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets, +                     llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> &BaseOffsets, +                     llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> &VirtualBaseOffsets) override +    { +        return false; +    } + +    void +    StartTranslationUnit(clang::ASTConsumer *Consumer) override +    { +        clang::TranslationUnitDecl *translation_unit_decl = m_decl_vendor.m_ast_ctx.getASTContext()->getTranslationUnitDecl(); +        translation_unit_decl->setHasExternalVisibleStorage(); +        translation_unit_decl->setHasExternalLexicalStorage(); +    } +private: +    AppleObjCDeclVendor                                    &m_decl_vendor; +}; + +AppleObjCDeclVendor::AppleObjCDeclVendor(ObjCLanguageRuntime &runtime) : +    DeclVendor(), +    m_runtime(runtime), +    m_ast_ctx(runtime.GetProcess()->GetTarget().GetArchitecture().GetTriple().getTriple().c_str()), +    m_type_realizer_sp(m_runtime.GetEncodingToType()) +{ +    m_external_source = new AppleObjCExternalASTSource (*this); +    llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> external_source_owning_ptr (m_external_source); +    m_ast_ctx.getASTContext()->setExternalSource(external_source_owning_ptr); +} + +clang::ObjCInterfaceDecl* +AppleObjCDeclVendor::GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa) +{ +    ISAToInterfaceMap::const_iterator iter = m_isa_to_interface.find(isa); +     +    if (iter != m_isa_to_interface.end()) +        return iter->second; +     +    clang::ASTContext *ast_ctx = m_ast_ctx.getASTContext(); +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor = m_runtime.GetClassDescriptorFromISA(isa); +     +    if (!descriptor) +        return NULL; +     +    const ConstString &name(descriptor->GetClassName()); +     +    clang::IdentifierInfo &identifier_info = ast_ctx->Idents.get(name.GetStringRef()); + +    clang::ObjCInterfaceDecl *new_iface_decl = clang::ObjCInterfaceDecl::Create(*ast_ctx, +                                                                                ast_ctx->getTranslationUnitDecl(), +                                                                                clang::SourceLocation(), +                                                                                &identifier_info, +                                                                                nullptr, +                                                                                nullptr); +     +    ClangASTMetadata meta_data; +    meta_data.SetISAPtr(isa); +    m_external_source->SetMetadata(new_iface_decl, meta_data); +     +    new_iface_decl->setHasExternalVisibleStorage(); +    new_iface_decl->setHasExternalLexicalStorage(); +     +    ast_ctx->getTranslationUnitDecl()->addDecl(new_iface_decl); +     +    m_isa_to_interface[isa] = new_iface_decl; +     +    return new_iface_decl; +} + +class ObjCRuntimeMethodType +{ +public: +    ObjCRuntimeMethodType (const char *types) : m_is_valid(false) +    { +        const char *cursor = types; +        enum ParserState { +            Start = 0, +            InType, +            InPos +        } state = Start; +        const char *type = NULL; +        int brace_depth = 0; +         +        uint32_t stepsLeft = 256; +         +        while (1) +        { +            if (--stepsLeft == 0) +            { +                m_is_valid = false; +                return; +            } +             +            switch (state) +            { +            case Start: +                { +                    switch (*cursor) +                    { +                    default: +                        state = InType; +                        type = cursor; +                        break; +                    case '\0': +                        m_is_valid = true; +                        return; +                    case '0': case '1': case '2': case '3': case '4': +                    case '5': case '6': case '7': case '8': case '9': +                        m_is_valid = false; +                        return; +                    } +                } +                break; +            case InType: +                { +                    switch (*cursor) +                    { +                    default: +                        ++cursor; +                        break; +                    case '0': case '1': case '2': case '3': case '4': +                    case '5': case '6': case '7': case '8': case '9': +                        if (!brace_depth) +                        { +                            state = InPos; +                            if (type) +                            { +                                m_type_vector.push_back(std::string(type, (cursor - type))); +                            } +                            else +                            { +                                m_is_valid = false; +                                return; +                            } +                            type = NULL; +                        } +                        else +                        { +                            ++cursor; +                        } +                        break; +                    case '[': case '{': case '(': +                        ++brace_depth; +                        ++cursor; +                        break; +                    case ']': case '}': case ')': +                        if (!brace_depth) +                        { +                            m_is_valid = false; +                            return; +                        } +                        --brace_depth; +                        ++cursor; +                        break; +                    case '\0': +                        m_is_valid = false; +                        return; +                    } +                } +                break; +            case InPos: +                { +                    switch (*cursor) +                    { +                    default: +                        state = InType; +                        type = cursor; +                        break; +                    case '0': case '1': case '2': case '3': case '4': +                    case '5': case '6': case '7': case '8': case '9': +                        ++cursor; +                        break; +                    case '\0': +                        m_is_valid = true; +                        return; +                    } +                } +                break; +            } +        } +    } +     +    clang::ObjCMethodDecl *BuildMethod (clang::ObjCInterfaceDecl *interface_decl, const char *name, bool instance, ObjCLanguageRuntime::EncodingToTypeSP type_realizer_sp) +    { +        if (!m_is_valid || m_type_vector.size() < 3) +            return NULL; +         +        clang::ASTContext &ast_ctx(interface_decl->getASTContext()); +         +        clang::QualType return_qual_type; +         +        const bool isInstance = instance; +        const bool isVariadic = false; +        const bool isSynthesized = false; +        const bool isImplicitlyDeclared = true; +        const bool isDefined = false; +        const clang::ObjCMethodDecl::ImplementationControl impControl = clang::ObjCMethodDecl::None; +        const bool HasRelatedResultType = false; +        const bool for_expression = true; +         +        std::vector <clang::IdentifierInfo *> selector_components; +         +        const char *name_cursor = name; +        bool is_zero_argument = true; +         +         +        while (*name_cursor != '\0') +        { +            const char *colon_loc = strchr(name_cursor, ':'); +            if (!colon_loc) +            { +                selector_components.push_back(&ast_ctx.Idents.get(llvm::StringRef(name_cursor))); +                break; +            } +            else +            { +                is_zero_argument = false; +                selector_components.push_back(&ast_ctx.Idents.get(llvm::StringRef(name_cursor, colon_loc - name_cursor))); +                name_cursor = colon_loc + 1; +            } +        } +         +        clang::Selector sel = ast_ctx.Selectors.getSelector(is_zero_argument ? 0 : selector_components.size(), selector_components.data()); +         +        clang::QualType ret_type = ClangASTContext::GetQualType(type_realizer_sp->RealizeType(interface_decl->getASTContext(), m_type_vector[0].c_str(), for_expression)); +         +        if (ret_type.isNull()) +            return NULL; +         +        clang::ObjCMethodDecl *ret = clang::ObjCMethodDecl::Create(ast_ctx, +                                                                   clang::SourceLocation(), +                                                                   clang::SourceLocation(), +                                                                   sel, +                                                                   ret_type, +                                                                   NULL, +                                                                   interface_decl, +                                                                   isInstance, +                                                                   isVariadic, +                                                                   isSynthesized, +                                                                   isImplicitlyDeclared, +                                                                   isDefined, +                                                                   impControl, +                                                                   HasRelatedResultType); +         +        std::vector <clang::ParmVarDecl*> parm_vars; +         +        for (size_t ai = 3, ae = m_type_vector.size(); +             ai != ae; +             ++ai) +        { +            const bool for_expression = true; +            clang::QualType arg_type = ClangASTContext::GetQualType(type_realizer_sp->RealizeType(ast_ctx, m_type_vector[ai].c_str(), for_expression)); +             +            if (arg_type.isNull()) +                return NULL; // well, we just wasted a bunch of time.  Wish we could delete the stuff we'd just made! + +            parm_vars.push_back(clang::ParmVarDecl::Create(ast_ctx, +                                                           ret, +                                                           clang::SourceLocation(), +                                                           clang::SourceLocation(), +                                                           NULL, +                                                           arg_type, +                                                           NULL, +                                                           clang::SC_None, +                                                           NULL)); +        } +         +        ret->setMethodParams(ast_ctx, llvm::ArrayRef<clang::ParmVarDecl*>(parm_vars), llvm::ArrayRef<clang::SourceLocation>()); +         +        return ret; +    } +private: +    typedef std::vector <std::string> TypeVector; +     +    TypeVector  m_type_vector; +    bool        m_is_valid; +}; + +bool +AppleObjCDeclVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl) +{ +    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));  // FIXME - a more appropriate log channel? +     +    ClangASTMetadata *metadata = m_external_source->GetMetadata(interface_decl); +    ObjCLanguageRuntime::ObjCISA objc_isa = 0; +    if (metadata) +     objc_isa = metadata->GetISAPtr(); +     +    if (!objc_isa) +        return false; +     +    if (!interface_decl->hasExternalVisibleStorage()) +        return true; +     +    interface_decl->startDefinition(); +     +    interface_decl->setHasExternalVisibleStorage(false); +    interface_decl->setHasExternalLexicalStorage(false); +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor = m_runtime.GetClassDescriptorFromISA(objc_isa); +     +    if (!descriptor) +        return false; +     +    auto superclass_func = [interface_decl, this](ObjCLanguageRuntime::ObjCISA isa) +    { +        clang::ObjCInterfaceDecl *superclass_decl = GetDeclForISA(isa); +         +        if (!superclass_decl) +            return; +         +        FinishDecl(superclass_decl); +        clang::ASTContext *context = m_ast_ctx.getASTContext(); +        interface_decl->setSuperClass( +                context->getTrivialTypeSourceInfo(context->getObjCInterfaceType(superclass_decl))); +    }; +     +    auto instance_method_func = [log, interface_decl, this](const char *name, const char *types) -> bool +    {         +        if (!name || !types) +            return false; // skip this one + +        ObjCRuntimeMethodType method_type(types); +         +        clang::ObjCMethodDecl *method_decl = method_type.BuildMethod (interface_decl, name, true, m_type_realizer_sp); +         +        if (log) +            log->Printf("[  AOTV::FD] Instance method [%s] [%s]", name, types); +         +        if (method_decl) +            interface_decl->addDecl(method_decl); +         +        return false; +    }; +     +    auto class_method_func = [log, interface_decl, this](const char *name, const char *types) -> bool +    { +        if (!name || !types) +            return false; // skip this one +         +        ObjCRuntimeMethodType method_type(types); +         +        clang::ObjCMethodDecl *method_decl = method_type.BuildMethod (interface_decl, name, false, m_type_realizer_sp); +         +        if (log) +            log->Printf("[  AOTV::FD] Class method [%s] [%s]", name, types); +         +        if (method_decl) +            interface_decl->addDecl(method_decl); +         +        return false; +    }; +     +    auto ivar_func = [log, interface_decl, this](const char *name, const char *type, lldb::addr_t offset_ptr, uint64_t size) -> bool +    { +        if (!name || !type) +            return false; +         +        const bool for_expression = false; +         +        if (log) +            log->Printf("[  AOTV::FD] Instance variable [%s] [%s], offset at %" PRIx64, name, type, offset_ptr); +         +        CompilerType ivar_type = m_runtime.GetEncodingToType()->RealizeType(m_ast_ctx, type, for_expression); +         +        if (ivar_type.IsValid()) +        { +            clang::TypeSourceInfo * const type_source_info = nullptr; +            const bool is_synthesized = false; +            clang::ObjCIvarDecl *ivar_decl = clang::ObjCIvarDecl::Create (*m_ast_ctx.getASTContext(), +                                                                          interface_decl, +                                                                          clang::SourceLocation(), +                                                                          clang::SourceLocation(), +                                                                          &m_ast_ctx.getASTContext()->Idents.get(name), +                                                                          ClangASTContext::GetQualType(ivar_type), +                                                                          type_source_info,                      // TypeSourceInfo * +                                                                          clang::ObjCIvarDecl::Public, +                                                                          0, +                                                                          is_synthesized); +             +            if (ivar_decl) +            { +                interface_decl->addDecl(ivar_decl); +            } +        } +         +        return false; +    }; +     +    if (log) +    { +        ASTDumper method_dumper ((clang::Decl*)interface_decl); +         +        log->Printf("[AppleObjCDeclVendor::FinishDecl] Finishing Objective-C interface for %s", descriptor->GetClassName().AsCString()); +    } +     +     +    if (!descriptor->Describe(superclass_func, +                              instance_method_func, +                              class_method_func, +                              ivar_func)) +        return false; +     +    if (log) +    { +        ASTDumper method_dumper ((clang::Decl*)interface_decl); +         +        log->Printf("[AppleObjCDeclVendor::FinishDecl] Finished Objective-C interface"); +         +        method_dumper.ToLog(log, "  [AOTV::FD] "); +    } +     +    return true; +} + +uint32_t +AppleObjCDeclVendor::FindDecls (const ConstString &name, +                                bool append, +                                uint32_t max_matches, +                                std::vector <clang::NamedDecl *> &decls) +{ +    static unsigned int invocation_id = 0; +    unsigned int current_id = invocation_id++; +     +    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));  // FIXME - a more appropriate log channel? +     +    if (log) +        log->Printf("AppleObjCDeclVendor::FindTypes [%u] ('%s', %s, %u, )", +                    current_id, +                    (const char*)name.AsCString(), +                    append ? "true" : "false", +                    max_matches); +     +    if (!append) +        decls.clear(); +     +    uint32_t ret = 0; +     +    do +    { +        // See if the type is already in our ASTContext. +         +        clang::ASTContext *ast_ctx = m_ast_ctx.getASTContext(); +         +        clang::IdentifierInfo &identifier_info = ast_ctx->Idents.get(name.GetStringRef()); +        clang::DeclarationName decl_name = ast_ctx->DeclarationNames.getIdentifier(&identifier_info); +         +        clang::DeclContext::lookup_result lookup_result = ast_ctx->getTranslationUnitDecl()->lookup(decl_name); +         +        if (!lookup_result.empty()) +        { +            if (clang::ObjCInterfaceDecl *result_iface_decl = llvm::dyn_cast<clang::ObjCInterfaceDecl>(lookup_result[0])) +            { +                if (log) +                { +                    clang::QualType result_iface_type = ast_ctx->getObjCInterfaceType(result_iface_decl); +                    ASTDumper dumper(result_iface_type); +                     +                    uint64_t isa_value = LLDB_INVALID_ADDRESS; +                    ClangASTMetadata *metadata = m_external_source->GetMetadata(result_iface_decl); +                    if (metadata) +                        isa_value = metadata->GetISAPtr(); +                     +                    log->Printf("AOCTV::FT [%u] Found %s (isa 0x%" PRIx64 ") in the ASTContext", +                                current_id, +                                dumper.GetCString(), +                                isa_value); +                } +                     +                decls.push_back(result_iface_decl); +                ret++; +                break; +            } +            else +            { +                if (log) +                    log->Printf("AOCTV::FT [%u] There's something in the ASTContext, but it's not something we know about", +                                current_id); +                break; +            } +        } +        else if(log) +        { +            log->Printf("AOCTV::FT [%u] Couldn't find %s in the ASTContext", +                        current_id, +                        name.AsCString()); +        } +         +        // It's not.  If it exists, we have to put it into our ASTContext. +                 +        ObjCLanguageRuntime::ObjCISA isa = m_runtime.GetISA(name); +     +        if (!isa) +        { +            if (log) +                log->Printf("AOCTV::FT [%u] Couldn't find the isa", +                            current_id); +             +            break; +        } +         +        clang::ObjCInterfaceDecl *iface_decl = GetDeclForISA(isa); +         +        if (!iface_decl) +        { +            if (log) +                log->Printf("AOCTV::FT [%u] Couldn't get the Objective-C interface for isa 0x%" PRIx64, +                            current_id, +                            (uint64_t)isa); +             +            break; +        } +         +        if (log) +        { +            clang::QualType new_iface_type = ast_ctx->getObjCInterfaceType(iface_decl); +            ASTDumper dumper(new_iface_type); +            log->Printf("AOCTV::FT [%u] Created %s (isa 0x%" PRIx64 ")", +                        current_id, +                        dumper.GetCString(), +                        (uint64_t)isa); +        } +         +        decls.push_back(iface_decl); +        ret++; +        break; +    } while (0); +     +    return ret; +} diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h new file mode 100644 index 0000000000000..88789c7b5a8d9 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h @@ -0,0 +1,55 @@ +//===-- AppleObjCDeclVendor.h -----------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AppleObjCDeclVendor_h_ +#define liblldb_AppleObjCDeclVendor_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/DeclVendor.h" +#include "lldb/Target/ObjCLanguageRuntime.h" + +namespace lldb_private { + +class AppleObjCExternalASTSource; +     +class AppleObjCDeclVendor : public DeclVendor +{ +public: +    AppleObjCDeclVendor(ObjCLanguageRuntime &runtime); +     +    uint32_t +    FindDecls(const ConstString &name, +              bool append, +              uint32_t max_matches, +              std::vector <clang::NamedDecl *> &decls) override; +         +    friend class AppleObjCExternalASTSource; + +private: +    clang::ObjCInterfaceDecl   *GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa); +    bool                        FinishDecl(clang::ObjCInterfaceDecl *decl); +     +    ObjCLanguageRuntime        &m_runtime; +    ClangASTContext             m_ast_ctx; +    ObjCLanguageRuntime::EncodingToTypeSP m_type_realizer_sp; +    AppleObjCExternalASTSource *m_external_source; +     +    typedef llvm::DenseMap<ObjCLanguageRuntime::ObjCISA, clang::ObjCInterfaceDecl *> ISAToInterfaceMap; + +    ISAToInterfaceMap           m_isa_to_interface; +}; + +} // namespace lldb_private + +#endif // liblldb_AppleObjCDeclVendor_h_ diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp new file mode 100644 index 0000000000000..cdb95250b2f4c --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp @@ -0,0 +1,541 @@ +//===-- AppleObjCRuntime.cpp -------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleObjCRuntime.h" +#include "AppleObjCTrampolineHandler.h" + +#include "clang/AST/Type.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include <vector> + +using namespace lldb; +using namespace lldb_private; + +#define PO_FUNCTION_TIMEOUT_USEC 15*1000*1000 + +AppleObjCRuntime::~AppleObjCRuntime() +{ +} + +AppleObjCRuntime::AppleObjCRuntime(Process *process) : +    ObjCLanguageRuntime (process), +    m_read_objc_library (false), +    m_objc_trampoline_handler_ap (), +    m_Foundation_major() +{ +    ReadObjCLibraryIfNeeded (process->GetTarget().GetImages()); +} + +bool +AppleObjCRuntime::GetObjectDescription (Stream &str, ValueObject &valobj) +{ +    CompilerType compiler_type(valobj.GetCompilerType()); +    bool is_signed; +    // ObjC objects can only be pointers (or numbers that actually represents pointers +    // but haven't been typecast, because reasons..) +    if (!compiler_type.IsIntegerType (is_signed) && !compiler_type.IsPointerType ()) +        return false; +     +    // Make the argument list: we pass one arg, the address of our pointer, to the print function. +    Value val; +     +    if (!valobj.ResolveValue(val.GetScalar())) +        return false; +     +    ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); +    return GetObjectDescription(str, val, exe_ctx.GetBestExecutionContextScope()); +                    +} +bool +AppleObjCRuntime::GetObjectDescription (Stream &strm, Value &value, ExecutionContextScope *exe_scope) +{ +    if (!m_read_objc_library) +        return false; +         +    ExecutionContext exe_ctx; +    exe_scope->CalculateExecutionContext(exe_ctx); +    Process *process = exe_ctx.GetProcessPtr(); +    if (!process) +        return false; +     +    // We need other parts of the exe_ctx, but the processes have to match. +    assert (m_process == process); +     +    // Get the function address for the print function. +    const Address *function_address = GetPrintForDebuggerAddr(); +    if (!function_address) +        return false; +     +    Target *target = exe_ctx.GetTargetPtr(); +    CompilerType compiler_type = value.GetCompilerType(); +    if (compiler_type) +    { +        if (!ClangASTContext::IsObjCObjectPointerType(compiler_type)) +        { +            strm.Printf ("Value doesn't point to an ObjC object.\n"); +            return false; +        } +    } +    else  +    { +        // If it is not a pointer, see if we can make it into a pointer. +        ClangASTContext *ast_context = target->GetScratchClangASTContext(); +        CompilerType opaque_type = ast_context->GetBasicType(eBasicTypeObjCID); +        if (!opaque_type) +            opaque_type = ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); +        //value.SetContext(Value::eContextTypeClangType, opaque_type_ptr); +        value.SetCompilerType (opaque_type); +    } + +    ValueList arg_value_list; +    arg_value_list.PushValue(value); +     +    // This is the return value: +    ClangASTContext *ast_context = target->GetScratchClangASTContext(); +     +    CompilerType return_compiler_type = ast_context->GetCStringType(true); +    Value ret; +//    ret.SetContext(Value::eContextTypeClangType, return_compiler_type); +    ret.SetCompilerType (return_compiler_type); +     +    if (exe_ctx.GetFramePtr() == NULL) +    { +        Thread *thread = exe_ctx.GetThreadPtr(); +        if (thread == NULL) +        { +            exe_ctx.SetThreadSP(process->GetThreadList().GetSelectedThread()); +            thread = exe_ctx.GetThreadPtr(); +        } +        if (thread) +        { +            exe_ctx.SetFrameSP(thread->GetSelectedFrame()); +        } +    } +     +    // Now we're ready to call the function: +     +    StreamString error_stream; +    lldb::addr_t wrapper_struct_addr = LLDB_INVALID_ADDRESS; + +    if (!m_print_object_caller_up) +    { +        Error error; +         m_print_object_caller_up.reset(exe_scope->CalculateTarget()->GetFunctionCallerForLanguage (eLanguageTypeObjC, +                                                                                                    return_compiler_type, +                                                                                                    *function_address, +                                                                                                    arg_value_list, +                                                                                                    "objc-object-description", +                                                                                                    error)); +        if (error.Fail()) +        { +            m_print_object_caller_up.reset(); +            strm.Printf("Could not get function runner to call print for debugger function: %s.", error.AsCString()); +            return false; +        } +        m_print_object_caller_up->InsertFunction(exe_ctx, wrapper_struct_addr, error_stream); +    } +    else +    { +        m_print_object_caller_up->WriteFunctionArguments(exe_ctx, +                                                         wrapper_struct_addr, +                                                         arg_value_list, +                                                         error_stream); +    } + +     + +    EvaluateExpressionOptions options; +    options.SetUnwindOnError(true); +    options.SetTryAllThreads(true); +    options.SetStopOthers(true); +    options.SetIgnoreBreakpoints(true); +    options.SetTimeoutUsec(PO_FUNCTION_TIMEOUT_USEC); +     +    ExpressionResults results = m_print_object_caller_up->ExecuteFunction (exe_ctx, +                                                                           &wrapper_struct_addr, +                                                                           options, +                                                                           error_stream, +                                                                           ret); +    if (results != eExpressionCompleted) +    { +        strm.Printf("Error evaluating Print Object function: %d.\n", results); +        return false; +    } +        +    addr_t result_ptr = ret.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); +     +    char buf[512]; +    size_t cstr_len = 0;     +    size_t full_buffer_len = sizeof (buf) - 1; +    size_t curr_len = full_buffer_len; +    while (curr_len == full_buffer_len) +    { +        Error error; +        curr_len = process->ReadCStringFromMemory(result_ptr + cstr_len, buf, sizeof(buf), error); +        strm.Write (buf, curr_len); +        cstr_len += curr_len; +    } +    return cstr_len > 0; +} + +lldb::ModuleSP +AppleObjCRuntime::GetObjCModule () +{ +    ModuleSP module_sp (m_objc_module_wp.lock()); +    if (module_sp) +        return module_sp; + +    Process *process = GetProcess(); +    if (process) +    { +        const ModuleList& modules = process->GetTarget().GetImages(); +        for (uint32_t idx = 0; idx < modules.GetSize(); idx++) +        { +            module_sp = modules.GetModuleAtIndex(idx); +            if (AppleObjCRuntime::AppleIsModuleObjCLibrary(module_sp)) +            { +                m_objc_module_wp = module_sp; +                return module_sp; +            } +        } +    } +    return ModuleSP(); +} + +Address * +AppleObjCRuntime::GetPrintForDebuggerAddr() +{ +    if (!m_PrintForDebugger_addr.get()) +    { +        const ModuleList &modules = m_process->GetTarget().GetImages(); +         +        SymbolContextList contexts; +        SymbolContext context; +         +        if ((!modules.FindSymbolsWithNameAndType(ConstString ("_NSPrintForDebugger"), eSymbolTypeCode, contexts)) && +           (!modules.FindSymbolsWithNameAndType(ConstString ("_CFPrintForDebugger"), eSymbolTypeCode, contexts))) +            return NULL; +         +        contexts.GetContextAtIndex(0, context); +         +        m_PrintForDebugger_addr.reset(new Address(context.symbol->GetAddress())); +    } +     +    return m_PrintForDebugger_addr.get(); +} + +bool +AppleObjCRuntime::CouldHaveDynamicValue (ValueObject &in_value) +{ +    return in_value.GetCompilerType().IsPossibleDynamicType (NULL, +                                                          false, // do not check C++ +                                                          true); // check ObjC +} + +bool +AppleObjCRuntime::GetDynamicTypeAndAddress (ValueObject &in_value,  +                                            lldb::DynamicValueType use_dynamic,  +                                            TypeAndOrName &class_type_or_name,  +                                            Address &address, +                                            Value::ValueType &value_type) +{ +    return false; +} + +TypeAndOrName +AppleObjCRuntime::FixUpDynamicType (const TypeAndOrName& type_and_or_name, +                                    ValueObject& static_value) +{ +    CompilerType static_type(static_value.GetCompilerType()); +    Flags static_type_flags(static_type.GetTypeInfo()); +     +    TypeAndOrName ret(type_and_or_name); +    if (type_and_or_name.HasType()) +    { +        // The type will always be the type of the dynamic object.  If our parent's type was a pointer, +        // then our type should be a pointer to the type of the dynamic object.  If a reference, then the original type +        // should be okay... +        CompilerType orig_type = type_and_or_name.GetCompilerType(); +        CompilerType corrected_type = orig_type; +        if (static_type_flags.AllSet(eTypeIsPointer)) +            corrected_type = orig_type.GetPointerType (); +        ret.SetCompilerType(corrected_type); +    } +    else +    { +        // If we are here we need to adjust our dynamic type name to include the correct & or * symbol +        std::string corrected_name (type_and_or_name.GetName().GetCString()); +        if (static_type_flags.AllSet(eTypeIsPointer)) +            corrected_name.append(" *"); +        // the parent type should be a correctly pointer'ed or referenc'ed type +        ret.SetCompilerType(static_type); +        ret.SetName(corrected_name.c_str()); +    } +    return ret; +} + +bool +AppleObjCRuntime::AppleIsModuleObjCLibrary (const ModuleSP &module_sp) +{ +    if (module_sp) +    { +        const FileSpec &module_file_spec = module_sp->GetFileSpec(); +        static ConstString ObjCName ("libobjc.A.dylib"); +         +        if (module_file_spec) +        { +            if (module_file_spec.GetFilename() == ObjCName) +                return true; +        } +    } +    return false; +} + +// we use the version of Foundation to make assumptions about the ObjC runtime on a target +uint32_t +AppleObjCRuntime::GetFoundationVersion () +{ +    if (!m_Foundation_major.hasValue()) +    { +        const ModuleList& modules = m_process->GetTarget().GetImages(); +        uint32_t major = UINT32_MAX; +        for (uint32_t idx = 0; idx < modules.GetSize(); idx++) +        { +            lldb::ModuleSP module_sp = modules.GetModuleAtIndex(idx); +            if (!module_sp) +                continue; +            if (strcmp(module_sp->GetFileSpec().GetFilename().AsCString(""),"Foundation") == 0) +            { +                module_sp->GetVersion(&major,1); +                m_Foundation_major = major; +                return major; +            } +        } +        return LLDB_INVALID_MODULE_VERSION; +    } +    else +        return m_Foundation_major.getValue(); +} + +bool +AppleObjCRuntime::IsModuleObjCLibrary (const ModuleSP &module_sp) +{ +    return AppleIsModuleObjCLibrary(module_sp); +} + +bool +AppleObjCRuntime::ReadObjCLibrary (const ModuleSP &module_sp) +{ +    // Maybe check here and if we have a handler already, and the UUID of this module is the same as the one in the +    // current module, then we don't have to reread it? +    m_objc_trampoline_handler_ap.reset(new AppleObjCTrampolineHandler (m_process->shared_from_this(), module_sp)); +    if (m_objc_trampoline_handler_ap.get() != NULL) +    { +        m_read_objc_library = true; +        return true; +    } +    else +        return false; +} + +ThreadPlanSP +AppleObjCRuntime::GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) +{ +    ThreadPlanSP thread_plan_sp; +    if (m_objc_trampoline_handler_ap.get()) +        thread_plan_sp = m_objc_trampoline_handler_ap->GetStepThroughDispatchPlan (thread, stop_others); +    return thread_plan_sp; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +ObjCLanguageRuntime::ObjCRuntimeVersions +AppleObjCRuntime::GetObjCVersion (Process *process, ModuleSP &objc_module_sp) +{ +    if (!process) +        return ObjCRuntimeVersions::eObjC_VersionUnknown; + +    Target &target = process->GetTarget(); +    if (target.GetArchitecture().GetTriple().getVendor() != llvm::Triple::VendorType::Apple) +        return ObjCRuntimeVersions::eObjC_VersionUnknown; + +    const ModuleList &target_modules = target.GetImages(); +    Mutex::Locker modules_locker(target_modules.GetMutex()); +     +    size_t num_images = target_modules.GetSize(); +    for (size_t i = 0; i < num_images; i++) +    { +        ModuleSP module_sp = target_modules.GetModuleAtIndexUnlocked(i); +        // One tricky bit here is that we might get called as part of the initial module loading, but +        // before all the pre-run libraries get winnowed from the module list.  So there might actually +        // be an old and incorrect ObjC library sitting around in the list, and we don't want to look at that. +        // That's why we call IsLoadedInTarget. +         +        if (AppleIsModuleObjCLibrary (module_sp) && module_sp->IsLoadedInTarget(&target)) +        { +            objc_module_sp = module_sp; +            ObjectFile *ofile = module_sp->GetObjectFile(); +            if (!ofile) +                return ObjCRuntimeVersions::eObjC_VersionUnknown; +             +            SectionList *sections = module_sp->GetSectionList(); +            if (!sections) +                return ObjCRuntimeVersions::eObjC_VersionUnknown; +            SectionSP v1_telltale_section_sp = sections->FindSectionByName(ConstString ("__OBJC")); +            if (v1_telltale_section_sp) +            { +                return ObjCRuntimeVersions::eAppleObjC_V1; +            } +            return ObjCRuntimeVersions::eAppleObjC_V2; +        } +    } +             +    return ObjCRuntimeVersions::eObjC_VersionUnknown; +} + +void +AppleObjCRuntime::SetExceptionBreakpoints () +{ +    const bool catch_bp = false; +    const bool throw_bp = true; +    const bool is_internal = true; +     +    if (!m_objc_exception_bp_sp) +    { +        m_objc_exception_bp_sp = LanguageRuntime::CreateExceptionBreakpoint (m_process->GetTarget(), +                                                                            GetLanguageType(), +                                                                            catch_bp,  +                                                                            throw_bp,  +                                                                            is_internal); +        if (m_objc_exception_bp_sp) +            m_objc_exception_bp_sp->SetBreakpointKind("ObjC exception"); +    } +    else +        m_objc_exception_bp_sp->SetEnabled(true); +} + + +void +AppleObjCRuntime::ClearExceptionBreakpoints () +{ +    if (!m_process) +        return; +     +    if (m_objc_exception_bp_sp.get()) +    { +        m_objc_exception_bp_sp->SetEnabled (false); +    } +} + +bool +AppleObjCRuntime::ExceptionBreakpointsAreSet () +{ +    return m_objc_exception_bp_sp && m_objc_exception_bp_sp->IsEnabled(); +} + +bool +AppleObjCRuntime::ExceptionBreakpointsExplainStop (lldb::StopInfoSP stop_reason) +{ +    if (!m_process) +        return false; +     +    if (!stop_reason ||  +        stop_reason->GetStopReason() != eStopReasonBreakpoint) +        return false; +     +    uint64_t break_site_id = stop_reason->GetValue(); +    return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint (break_site_id, +                                                                                m_objc_exception_bp_sp->GetID()); +} + +bool +AppleObjCRuntime::CalculateHasNewLiteralsAndIndexing() +{ +    if (!m_process) +        return false; +     +    Target &target(m_process->GetTarget()); +     +    static ConstString s_method_signature("-[NSDictionary objectForKeyedSubscript:]"); +    static ConstString s_arclite_method_signature("__arclite_objectForKeyedSubscript"); +     +    SymbolContextList sc_list; +     +    if (target.GetImages().FindSymbolsWithNameAndType(s_method_signature, eSymbolTypeCode, sc_list) || +        target.GetImages().FindSymbolsWithNameAndType(s_arclite_method_signature, eSymbolTypeCode, sc_list)) +        return true; +    else +        return false; +} + +lldb::SearchFilterSP +AppleObjCRuntime::CreateExceptionSearchFilter () +{ +    Target &target = m_process->GetTarget(); +     +    if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) +    { +        FileSpecList filter_modules; +        filter_modules.Append(FileSpec("libobjc.A.dylib", false)); +        return target.GetSearchFilterForModuleList(&filter_modules); +    } +    else +    { +        return LanguageRuntime::CreateExceptionSearchFilter(); +    } +} + +void +AppleObjCRuntime::ReadObjCLibraryIfNeeded (const ModuleList &module_list) +{ +    if (!HasReadObjCLibrary ()) +    { +        Mutex::Locker locker (module_list.GetMutex ()); + +        size_t num_modules = module_list.GetSize(); +        for (size_t i = 0; i < num_modules; i++) +        { +            auto mod = module_list.GetModuleAtIndex (i); +            if (IsModuleObjCLibrary (mod)) +            { +                ReadObjCLibrary (mod); +                break; +            } +        } +    } +} + +void +AppleObjCRuntime::ModulesDidLoad (const ModuleList &module_list) +{ +    ReadObjCLibraryIfNeeded (module_list); +} + diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h new file mode 100644 index 0000000000000..342824e79b1f7 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h @@ -0,0 +1,148 @@ +//===-- AppleObjCRuntime.h --------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AppleObjCRuntime_h_ +#define liblldb_AppleObjCRuntime_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/Optional.h" + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "AppleObjCTrampolineHandler.h" +#include "AppleThreadPlanStepThroughObjCTrampoline.h" + +namespace lldb_private { +     +class AppleObjCRuntime : +        public lldb_private::ObjCLanguageRuntime +{ +public: +    ~AppleObjCRuntime() override; + +    //------------------------------------------------------------------ +    // Static Functions +    //------------------------------------------------------------------ +    // Note there is no CreateInstance, Initialize & Terminate functions here, because +    // you can't make an instance of this generic runtime. + +    static bool classof(const ObjCLanguageRuntime* runtime) +    { +        switch (runtime->GetRuntimeVersion()) +        { +            case ObjCRuntimeVersions::eAppleObjC_V1: +            case ObjCRuntimeVersions::eAppleObjC_V2: +                return true; +            default: +                return false; +        } +    } +     +    // These are generic runtime functions: +    bool +    GetObjectDescription (Stream &str, Value &value, ExecutionContextScope *exe_scope) override; +     +    bool +    GetObjectDescription (Stream &str, ValueObject &object) override; +     +    bool +    CouldHaveDynamicValue (ValueObject &in_value) override; +     +    bool +    GetDynamicTypeAndAddress (ValueObject &in_value,  +                              lldb::DynamicValueType use_dynamic,  +                              TypeAndOrName &class_type_or_name,  +                              Address &address, +                              Value::ValueType &value_type) override; + +    TypeAndOrName +    FixUpDynamicType (const TypeAndOrName& type_and_or_name, +                      ValueObject& static_value) override; +     +    // These are the ObjC specific functions. +     +    bool +    IsModuleObjCLibrary (const lldb::ModuleSP &module_sp) override; +     +    bool +    ReadObjCLibrary (const lldb::ModuleSP &module_sp) override; + +    bool +    HasReadObjCLibrary ()  override +    { +        return m_read_objc_library; +    } +     +    lldb::ThreadPlanSP +    GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) override; +     +    // Get the "libobjc.A.dylib" module from the current target if we can find +    // it, also cache it once it is found to ensure quick lookups. +    lldb::ModuleSP +    GetObjCModule (); +     +    // Sync up with the target + +    void +    ModulesDidLoad (const ModuleList &module_list) override; + +    void +    SetExceptionBreakpoints() override; + +    void +    ClearExceptionBreakpoints() override; +     +    bool +    ExceptionBreakpointsAreSet() override; +     +    bool +    ExceptionBreakpointsExplainStop(lldb::StopInfoSP stop_reason) override; +     +    lldb::SearchFilterSP +    CreateExceptionSearchFilter() override; +     +    uint32_t +    GetFoundationVersion(); +     +protected: +    // Call CreateInstance instead. +    AppleObjCRuntime(Process *process); + +    bool +    CalculateHasNewLiteralsAndIndexing() override; +     +    static bool +    AppleIsModuleObjCLibrary(const lldb::ModuleSP &module_sp); + +    static ObjCRuntimeVersions +    GetObjCVersion(Process *process, lldb::ModuleSP &objc_module_sp); + +    void +    ReadObjCLibraryIfNeeded(const ModuleList &module_list); + +    Address * +    GetPrintForDebuggerAddr(); +     +    std::unique_ptr<Address>  m_PrintForDebugger_addr; +    bool m_read_objc_library; +    std::unique_ptr<lldb_private::AppleObjCTrampolineHandler> m_objc_trampoline_handler_ap; +    lldb::BreakpointSP m_objc_exception_bp_sp; +    lldb::ModuleWP m_objc_module_wp; +    std::unique_ptr<FunctionCaller>  m_print_object_caller_up; +     +    llvm::Optional<uint32_t> m_Foundation_major; +}; +     +} // namespace lldb_private + +#endif // liblldb_AppleObjCRuntime_h_ diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp new file mode 100644 index 0000000000000..59b28b63f6b3a --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp @@ -0,0 +1,459 @@ +//===-- AppleObjCRuntimeV1.cpp --------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleObjCRuntimeV1.h" +#include "AppleObjCTrampolineHandler.h" +#include "AppleObjCDeclVendor.h" + +#include "clang/AST/Type.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include <vector> + +using namespace lldb; +using namespace lldb_private; + +AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process) : +    AppleObjCRuntime (process), +    m_hash_signature (), +    m_isa_hash_table_ptr (LLDB_INVALID_ADDRESS) +{ +} + +// for V1 runtime we just try to return a class name as that is the minimum level of support +// required for the data formatters to work +bool +AppleObjCRuntimeV1::GetDynamicTypeAndAddress (ValueObject &in_value, +                                              lldb::DynamicValueType use_dynamic, +                                              TypeAndOrName &class_type_or_name, +                                              Address &address, +                                              Value::ValueType &value_type) +{ +    class_type_or_name.Clear(); +    value_type = Value::ValueType::eValueTypeScalar; +    if (CouldHaveDynamicValue(in_value)) +    { +        auto class_descriptor(GetClassDescriptor(in_value)); +        if (class_descriptor && class_descriptor->IsValid() && class_descriptor->GetClassName()) +        { +            const addr_t object_ptr = in_value.GetPointerValue(); +            address.SetRawAddress(object_ptr); +            class_type_or_name.SetName(class_descriptor->GetClassName()); +        } +    } +    return class_type_or_name.IsEmpty() == false; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +lldb_private::LanguageRuntime * +AppleObjCRuntimeV1::CreateInstance (Process *process, lldb::LanguageType language) +{ +    // FIXME: This should be a MacOS or iOS process, and we need to look for the OBJC section to make +    // sure we aren't using the V1 runtime. +    if (language == eLanguageTypeObjC) +    { +        ModuleSP objc_module_sp; +         +        if (AppleObjCRuntime::GetObjCVersion (process, objc_module_sp) == ObjCRuntimeVersions::eAppleObjC_V1) +            return new AppleObjCRuntimeV1 (process); +        else +            return NULL; +    } +    else +        return NULL; +} + + +void +AppleObjCRuntimeV1::Initialize() +{ +    PluginManager::RegisterPlugin (GetPluginNameStatic(), +                                   "Apple Objective C Language Runtime - Version 1", +                                   CreateInstance);     +} + +void +AppleObjCRuntimeV1::Terminate() +{ +    PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +AppleObjCRuntimeV1::GetPluginNameStatic() +{ +    static ConstString g_name("apple-objc-v1"); +    return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString +AppleObjCRuntimeV1::GetPluginName() +{ +    return GetPluginNameStatic(); +} + +uint32_t +AppleObjCRuntimeV1::GetPluginVersion() +{ +    return 1; +} + +BreakpointResolverSP +AppleObjCRuntimeV1::CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp) +{ +    BreakpointResolverSP resolver_sp; +     +    if (throw_bp) +        resolver_sp.reset (new BreakpointResolverName (bkpt, +                                                       "objc_exception_throw", +                                                       eFunctionNameTypeBase, +                                                       eLanguageTypeUnknown, +                                                       Breakpoint::Exact, +                                                       eLazyBoolNo)); +    // FIXME: don't do catch yet. +    return resolver_sp; +} + +struct BufStruct { +    char contents[2048]; +}; + +UtilityFunction * +AppleObjCRuntimeV1::CreateObjectChecker(const char *name) +{ +    std::unique_ptr<BufStruct> buf(new BufStruct); +     +    assert(snprintf(&buf->contents[0], sizeof(buf->contents), +                    "struct __objc_class                                                    \n" +                    "{                                                                      \n" +                    "   struct __objc_class *isa;                                           \n" +                    "   struct __objc_class *super_class;                                   \n" +                    "   const char *name;                                                   \n" +                    "   // rest of struct elided because unused                             \n" +                    "};                                                                     \n" +                    "                                                                       \n" +                    "struct __objc_object                                                   \n" +                    "{                                                                      \n" +                    "   struct __objc_class *isa;                                           \n" +                    "};                                                                     \n" +                    "                                                                       \n" +                    "extern \"C\" void                                                      \n" +                    "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector)                  \n" +                    "{                                                                      \n" +                    "   struct __objc_object *obj = (struct __objc_object*)$__lldb_arg_obj; \n" +                    "   (int)strlen(obj->isa->name);                                        \n" +                    "}                                                                      \n", +                    name) < (int)sizeof(buf->contents)); + +    Error error; +    return GetTargetRef().GetUtilityFunctionForLanguage(buf->contents, eLanguageTypeObjC, name, error); +} + +AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ValueObject &isa_pointer) +{ +    Initialize (isa_pointer.GetValueAsUnsigned(0), +                isa_pointer.GetProcessSP()); +} + +AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ObjCISA isa, lldb::ProcessSP process_sp) +{ +    Initialize (isa, process_sp); +} + +void +AppleObjCRuntimeV1::ClassDescriptorV1::Initialize (ObjCISA isa, lldb::ProcessSP process_sp) +{ +    if (!isa || !process_sp) +    { +        m_valid = false; +        return; +    } +     +    m_valid = true; +     +    Error error; +     +    m_isa = process_sp->ReadPointerFromMemory(isa, error); +     +    if (error.Fail()) +    { +        m_valid = false; +        return; +    } +     +    uint32_t ptr_size = process_sp->GetAddressByteSize(); +     +    if (!IsPointerValid(m_isa,ptr_size)) +    { +        m_valid = false; +        return; +    } + +    m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size,error); +     +    if (error.Fail()) +    { +        m_valid = false; +        return; +    } +     +    if (!IsPointerValid(m_parent_isa,ptr_size,true)) +    { +        m_valid = false; +        return; +    } +     +    lldb::addr_t name_ptr = process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size,error); +     +    if (error.Fail()) +    { +        m_valid = false; +        return; +    } +     +    lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); +     +    size_t count = process_sp->ReadCStringFromMemory(name_ptr, (char*)buffer_sp->GetBytes(), 1024, error); +     +    if (error.Fail()) +    { +        m_valid = false; +        return; +    } +     +    if (count) +        m_name = ConstString((char*)buffer_sp->GetBytes()); +    else +        m_name = ConstString(); +     +    m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(m_isa + 5 * ptr_size, ptr_size, 0, error); +     +    if (error.Fail()) +    { +        m_valid = false; +        return; +    } +     +    m_process_wp = lldb::ProcessWP(process_sp); +} + +AppleObjCRuntime::ClassDescriptorSP +AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass () +{ +    if (!m_valid) +        return AppleObjCRuntime::ClassDescriptorSP(); +    ProcessSP process_sp = m_process_wp.lock(); +    if (!process_sp) +        return AppleObjCRuntime::ClassDescriptorSP(); +    return ObjCLanguageRuntime::ClassDescriptorSP(new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa,process_sp)); +} + +AppleObjCRuntime::ClassDescriptorSP +AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass () const +{ +    return ClassDescriptorSP(); +} + +bool +AppleObjCRuntimeV1::ClassDescriptorV1::Describe (std::function <void (ObjCLanguageRuntime::ObjCISA)> const &superclass_func, +                                                 std::function <bool (const char *, const char *)> const &instance_method_func, +                                                 std::function <bool (const char *, const char *)> const &class_method_func, +                                                 std::function <bool (const char *, const char *, lldb::addr_t, uint64_t)> const &ivar_func) const +{ +    return false; +} + +lldb::addr_t +AppleObjCRuntimeV1::GetISAHashTablePointer () +{ +    if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) +    { +        ModuleSP objc_module_sp(GetObjCModule()); +         +        if (!objc_module_sp) +            return LLDB_INVALID_ADDRESS; +         +        static ConstString g_objc_debug_class_hash("_objc_debug_class_hash"); +         +        const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(g_objc_debug_class_hash, lldb::eSymbolTypeData); +        if (symbol && symbol->ValueIsAddress()) +        { +            Process *process = GetProcess(); +            if (process) +            { + +                lldb::addr_t objc_debug_class_hash_addr = symbol->GetAddressRef().GetLoadAddress(&process->GetTarget()); +             +                if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) +                { +                    Error error; +                    lldb::addr_t objc_debug_class_hash_ptr = process->ReadPointerFromMemory(objc_debug_class_hash_addr, error); +                    if (objc_debug_class_hash_ptr != 0 && +                        objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS) +                    { +                        m_isa_hash_table_ptr = objc_debug_class_hash_ptr; +                    } +                } +            } +        } +    } +    return m_isa_hash_table_ptr; +} + +void +AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() +{ +    // TODO: implement HashTableSignature... +    Process *process = GetProcess(); +     +    if (process) +    { +        // Update the process stop ID that indicates the last time we updated the +        // map, whether it was successful or not. +        m_isa_to_descriptor_stop_id = process->GetStopID(); +         +        Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); +         +        ProcessSP process_sp = process->shared_from_this(); +         +        ModuleSP objc_module_sp(GetObjCModule()); +         +        if (!objc_module_sp) +            return; +         +        uint32_t isa_count = 0; +         +        lldb::addr_t hash_table_ptr = GetISAHashTablePointer (); +        if (hash_table_ptr != LLDB_INVALID_ADDRESS) +        { +            // Read the NXHashTable struct: +            // +            // typedef struct { +            //     const NXHashTablePrototype *prototype; +            //     unsigned   count; +            //     unsigned   nbBuckets; +            //     void       *buckets; +            //     const void *info; +            // } NXHashTable; + +            Error error; +            DataBufferHeap buffer(1024, 0); +            if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) == 20) +            { +                const uint32_t addr_size = m_process->GetAddressByteSize(); +                const ByteOrder byte_order = m_process->GetByteOrder(); +                DataExtractor data (buffer.GetBytes(), buffer.GetByteSize(), byte_order, addr_size); +                lldb::offset_t offset = addr_size; // Skip prototype +                const uint32_t count = data.GetU32(&offset); +                const uint32_t num_buckets = data.GetU32(&offset); +                const addr_t buckets_ptr = data.GetPointer(&offset); +                if (m_hash_signature.NeedsUpdate (count, num_buckets, buckets_ptr)) +                { +                    m_hash_signature.UpdateSignature (count, num_buckets, buckets_ptr); + +                    const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t); +                    buffer.SetByteSize(data_size); +                     +                    if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size, error) == data_size) +                    { +                        data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order); +                        offset = 0; +                        for (uint32_t bucket_idx = 0; bucket_idx < num_buckets; ++bucket_idx) +                        { +                            const uint32_t bucket_isa_count = data.GetU32 (&offset); +                            const lldb::addr_t bucket_data = data.GetU32 (&offset); +                             + +                            if (bucket_isa_count == 0) +                                continue; +                             +                            isa_count += bucket_isa_count; + +                            ObjCISA isa; +                            if (bucket_isa_count == 1) +                            { +                                // When we only have one entry in the bucket, the bucket data is the "isa" +                                isa = bucket_data; +                                if (isa) +                                { +                                    if (!ISAIsCached(isa)) +                                    { +                                        ClassDescriptorSP descriptor_sp (new ClassDescriptorV1(isa, process_sp)); +                                         +                                        if (log && log->GetVerbose()) +                                            log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 " from _objc_debug_class_hash to isa->descriptor cache", isa); +                                         +                                        AddClass (isa, descriptor_sp); +                                    } +                                } +                            } +                            else +                            { +                                // When we have more than one entry in the bucket, the bucket data is a pointer +                                // to an array of "isa" values +                                addr_t isa_addr = bucket_data; +                                for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count; ++isa_idx, isa_addr += addr_size) +                                { +                                    isa = m_process->ReadPointerFromMemory(isa_addr, error); + +                                    if (isa && isa != LLDB_INVALID_ADDRESS) +                                    { +                                        if (!ISAIsCached(isa)) +                                        { +                                            ClassDescriptorSP descriptor_sp (new ClassDescriptorV1(isa, process_sp)); +                                             +                                            if (log && log->GetVerbose()) +                                                log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 " from _objc_debug_class_hash to isa->descriptor cache", isa); +                                             +                                            AddClass (isa, descriptor_sp); +                                        } +                                    } +                                } +                            } +                        } +                    } +                } +            } +        }         +    } +    else +    { +        m_isa_to_descriptor_stop_id = UINT32_MAX; +    } +} + +DeclVendor * +AppleObjCRuntimeV1::GetDeclVendor() +{ +    if (!m_decl_vendor_ap.get()) +        m_decl_vendor_ap.reset(new AppleObjCDeclVendor(*this)); +     +    return m_decl_vendor_ap.get(); +} diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h new file mode 100644 index 0000000000000..9f9fcc69b682b --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h @@ -0,0 +1,207 @@ +//===-- AppleObjCRuntimeV1.h ------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AppleObjCRuntimeV1_h_ +#define liblldb_AppleObjCRuntimeV1_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "AppleObjCRuntime.h" + +namespace lldb_private { +     +class AppleObjCRuntimeV1 : +        public AppleObjCRuntime +{ +public: +    ~AppleObjCRuntimeV1() override = default; + +    //------------------------------------------------------------------ +    // Static Functions +    //------------------------------------------------------------------ +    static void +    Initialize(); +     +    static void +    Terminate(); +     +    static lldb_private::LanguageRuntime * +    CreateInstance(Process *process, lldb::LanguageType language); +     +    static lldb_private::ConstString +    GetPluginNameStatic(); + +    static bool classof(const ObjCLanguageRuntime* runtime) +    { +        switch (runtime->GetRuntimeVersion()) +        { +            case ObjCRuntimeVersions::eAppleObjC_V1: +                return true; +            default: +                return false; +        } +    } +     +    class ClassDescriptorV1 : public ObjCLanguageRuntime::ClassDescriptor +    { +    public: +        ClassDescriptorV1 (ValueObject &isa_pointer); +        ClassDescriptorV1 (ObjCISA isa, lldb::ProcessSP process_sp); +         +        ~ClassDescriptorV1() override = default; + +        ConstString +        GetClassName() override +        { +            return m_name; +        } +         +        ClassDescriptorSP +        GetSuperclass() override; +         +        ClassDescriptorSP +        GetMetaclass() const override; +         +        bool +        IsValid() override +        { +            return m_valid; +        } +         +        // v1 does not support tagged pointers +        bool +        GetTaggedPointerInfo(uint64_t* info_bits = nullptr, +                             uint64_t* value_bits = nullptr, +                             uint64_t* payload = nullptr) override +        { +            return false; +        } +         +        uint64_t +        GetInstanceSize() override +        { +            return m_instance_size; +        } +         +        ObjCISA +        GetISA() override +        { +            return m_isa; +        } +         +        bool +        Describe(std::function <void (ObjCLanguageRuntime::ObjCISA)> const &superclass_func, +                 std::function <bool (const char *, const char *)> const &instance_method_func, +                 std::function <bool (const char *, const char *)> const &class_method_func, +                 std::function <bool (const char *, const char *, +                 lldb::addr_t, uint64_t)> const &ivar_func) const override; + +    protected: +        void +        Initialize (ObjCISA isa, lldb::ProcessSP process_sp); +         +    private: +        ConstString m_name; +        ObjCISA m_isa; +        ObjCISA m_parent_isa; +        bool m_valid; +        lldb::ProcessWP m_process_wp; +        uint64_t m_instance_size; +    }; + +    // These are generic runtime functions: +    bool +    GetDynamicTypeAndAddress(ValueObject &in_value,  +                             lldb::DynamicValueType use_dynamic,  +                             TypeAndOrName &class_type_or_name,  +                             Address &address, +                             Value::ValueType &value_type) override; + +    UtilityFunction * +    CreateObjectChecker(const char *) override; + +    //------------------------------------------------------------------ +    // PluginInterface protocol +    //------------------------------------------------------------------ +    ConstString +    GetPluginName() override; +     +    uint32_t +    GetPluginVersion() override; +     +    ObjCRuntimeVersions +    GetRuntimeVersion() const override +    { +        return ObjCRuntimeVersions::eAppleObjC_V1; +    } +     +    void +    UpdateISAToDescriptorMapIfNeeded() override; +     +    DeclVendor * +    GetDeclVendor() override; + +protected: +    lldb::BreakpointResolverSP +    CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp, bool throw_bp) override; +     +    class HashTableSignature +    { +    public: +        HashTableSignature () : +            m_count (0), +            m_num_buckets (0), +            m_buckets_ptr (LLDB_INVALID_ADDRESS) +        { +        } +         +        bool +        NeedsUpdate (uint32_t count, +                     uint32_t num_buckets, +                     lldb::addr_t buckets_ptr) +        { +            return m_count       != count       || +                   m_num_buckets != num_buckets || +                   m_buckets_ptr != buckets_ptr ; +        } +         +        void +        UpdateSignature (uint32_t count, +                         uint32_t num_buckets, +                         lldb::addr_t buckets_ptr) +        { +            m_count = count; +            m_num_buckets = num_buckets; +            m_buckets_ptr = buckets_ptr; +        } + +    protected: +        uint32_t m_count; +        uint32_t m_num_buckets; +        lldb::addr_t m_buckets_ptr; +    }; +     +    lldb::addr_t +    GetISAHashTablePointer (); +     +    HashTableSignature m_hash_signature; +    lldb::addr_t m_isa_hash_table_ptr; +    std::unique_ptr<DeclVendor> m_decl_vendor_ap; + +private: +    AppleObjCRuntimeV1(Process *process); +}; +     +} // namespace lldb_private + +#endif // liblldb_AppleObjCRuntimeV1_h_ diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp new file mode 100644 index 0000000000000..8c485d97bdc95 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -0,0 +1,2215 @@ +//===-- AppleObjCRuntimeV2.cpp ----------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <stdint.h> + +// C++ Includes +#include <string> +#include <vector> + +// Other libraries and framework includes +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclObjC.h" + +// Project includes +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Symbol/CompilerType.h" + +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include "AppleObjCRuntimeV2.h" +#include "AppleObjCClassDescriptorV2.h" +#include "AppleObjCTypeEncodingParser.h" +#include "AppleObjCDeclVendor.h" +#include "AppleObjCTrampolineHandler.h" + +#if defined(__APPLE__) +#include "Plugins/Platform/MacOSX/PlatformiOSSimulator.h" +#endif + +using namespace lldb; +using namespace lldb_private; + +// 2 second timeout when running utility functions +#define UTILITY_FUNCTION_TIMEOUT_USEC 2*1000*1000 + +static const char *g_get_dynamic_class_info_name = "__lldb_apple_objc_v2_get_dynamic_class_info"; +// Testing using the new C++11 raw string literals. If this breaks GCC then we will +// need to revert to the code above... +static const char *g_get_dynamic_class_info_body = R"( + +extern "C" +{ +    size_t strlen(const char *); +    char *strncpy (char * s1, const char * s2, size_t n); +    int printf(const char * format, ...); +} +//#define ENABLE_DEBUG_PRINTF // COMMENT THIS LINE OUT PRIOR TO CHECKIN +#ifdef ENABLE_DEBUG_PRINTF +#define DEBUG_PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +typedef struct _NXMapTable { +    void *prototype; +    unsigned num_classes; +    unsigned num_buckets_minus_one; +    void *buckets; +} NXMapTable; + +#define NX_MAPNOTAKEY   ((void *)(-1)) + +typedef struct BucketInfo +{ +    const char *name_ptr; +    Class isa; +} BucketInfo; + +struct ClassInfo +{ +    Class isa; +    uint32_t hash; +} __attribute__((__packed__)); + +uint32_t +__lldb_apple_objc_v2_get_dynamic_class_info (void *gdb_objc_realized_classes_ptr, +                                             void *class_infos_ptr, +                                             uint32_t class_infos_byte_size) +{ +    DEBUG_PRINTF ("gdb_objc_realized_classes_ptr = %p\n", gdb_objc_realized_classes_ptr); +    DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr); +    DEBUG_PRINTF ("class_infos_byte_size = %u\n", class_infos_byte_size); +    const NXMapTable *grc = (const NXMapTable *)gdb_objc_realized_classes_ptr; +    if (grc) +    { +        const unsigned num_classes = grc->num_classes; +        if (class_infos_ptr) +        { +            const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo); +            ClassInfo *class_infos = (ClassInfo *)class_infos_ptr; +            BucketInfo *buckets = (BucketInfo *)grc->buckets; +             +            uint32_t idx = 0; +            for (unsigned i=0; i<=grc->num_buckets_minus_one; ++i) +            { +                if (buckets[i].name_ptr != NX_MAPNOTAKEY) +                { +                    if (idx < max_class_infos) +                    { +                        const char *s = buckets[i].name_ptr; +                        uint32_t h = 5381; +                        for (unsigned char c = *s; c; c = *++s) +                            h = ((h << 5) + h) + c; +                        class_infos[idx].hash = h; +                        class_infos[idx].isa = buckets[i].isa; +                    } +                    ++idx; +                } +            } +            if (idx < max_class_infos) +            { +                class_infos[idx].isa = NULL; +                class_infos[idx].hash = 0; +            } +        } +        return num_classes; +    } +    return 0; +} + +)"; + +static const char *g_get_shared_cache_class_info_name = "__lldb_apple_objc_v2_get_shared_cache_class_info"; +// Testing using the new C++11 raw string literals. If this breaks GCC then we will +// need to revert to the code above... +static const char *g_get_shared_cache_class_info_body = R"( + +extern "C" +{ +    const char *class_getName(void *objc_class); +    size_t strlen(const char *); +    char *strncpy (char * s1, const char * s2, size_t n); +    int printf(const char * format, ...); +} + +// #define ENABLE_DEBUG_PRINTF // COMMENT THIS LINE OUT PRIOR TO CHECKIN +#ifdef ENABLE_DEBUG_PRINTF +#define DEBUG_PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + + +struct objc_classheader_t { +    int32_t clsOffset; +    int32_t hiOffset; +}; + +struct objc_clsopt_t { +    uint32_t capacity; +    uint32_t occupied; +    uint32_t shift; +    uint32_t mask; +    uint32_t zero; +    uint32_t unused; +    uint64_t salt; +    uint32_t scramble[256]; +    uint8_t tab[0]; // tab[mask+1] +    //  uint8_t checkbytes[capacity]; +    //  int32_t offset[capacity]; +    //  objc_classheader_t clsOffsets[capacity]; +    //  uint32_t duplicateCount; +    //  objc_classheader_t duplicateOffsets[duplicateCount]; +}; + +struct objc_opt_t { +    uint32_t version; +    int32_t selopt_offset; +    int32_t headeropt_offset; +    int32_t clsopt_offset; +}; + +struct objc_opt_v14_t { +    uint32_t version; +    uint32_t flags; +    int32_t selopt_offset; +    int32_t headeropt_offset; +    int32_t clsopt_offset; +}; + +struct ClassInfo +{ +    Class isa; +    uint32_t hash; +}  __attribute__((__packed__)); + +uint32_t +__lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr, +                                                  void *class_infos_ptr, +                                                  uint32_t class_infos_byte_size) +{ +    uint32_t idx = 0; +    DEBUG_PRINTF ("objc_opt_ro_ptr = %p\n", objc_opt_ro_ptr); +    DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr); +    DEBUG_PRINTF ("class_infos_byte_size = %u (%" PRIu64 " class infos)\n", class_infos_byte_size, (size_t)(class_infos_byte_size/sizeof(ClassInfo))); +    if (objc_opt_ro_ptr) +    { +        const objc_opt_t *objc_opt = (objc_opt_t *)objc_opt_ro_ptr; +        const objc_opt_v14_t* objc_opt_v14 = (objc_opt_v14_t*)objc_opt_ro_ptr; +        const bool is_v14_format = objc_opt->version >= 14; +        if (is_v14_format) +        { +            DEBUG_PRINTF ("objc_opt->version = %u\n", objc_opt_v14->version); +            DEBUG_PRINTF ("objc_opt->flags = %u\n", objc_opt_v14->flags); +            DEBUG_PRINTF ("objc_opt->selopt_offset = %d\n", objc_opt_v14->selopt_offset); +            DEBUG_PRINTF ("objc_opt->headeropt_offset = %d\n", objc_opt_v14->headeropt_offset); +            DEBUG_PRINTF ("objc_opt->clsopt_offset = %d\n", objc_opt_v14->clsopt_offset); +        } +        else +        { +            DEBUG_PRINTF ("objc_opt->version = %u\n", objc_opt->version); +            DEBUG_PRINTF ("objc_opt->selopt_offset = %d\n", objc_opt->selopt_offset); +            DEBUG_PRINTF ("objc_opt->headeropt_offset = %d\n", objc_opt->headeropt_offset); +            DEBUG_PRINTF ("objc_opt->clsopt_offset = %d\n", objc_opt->clsopt_offset); +        } +        if (objc_opt->version == 12 || objc_opt->version == 13 || objc_opt->version == 14) +        { +            const objc_clsopt_t* clsopt = NULL; +            if (is_v14_format) +                clsopt = (const objc_clsopt_t*)((uint8_t *)objc_opt_v14 + objc_opt_v14->clsopt_offset); +            else +                clsopt = (const objc_clsopt_t*)((uint8_t *)objc_opt + objc_opt->clsopt_offset); +            const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo); +            ClassInfo *class_infos = (ClassInfo *)class_infos_ptr; +            int32_t invalidEntryOffset = 0; +            // this is safe to do because the version field order is invariant +            if (objc_opt->version == 12) +                invalidEntryOffset = 16; +            const uint8_t *checkbytes = &clsopt->tab[clsopt->mask+1]; +            const int32_t *offsets = (const int32_t *)(checkbytes + clsopt->capacity); +            const objc_classheader_t *classOffsets = (const objc_classheader_t *)(offsets + clsopt->capacity); +            DEBUG_PRINTF ("clsopt->capacity = %u\n", clsopt->capacity); +            DEBUG_PRINTF ("clsopt->mask = 0x%8.8x\n", clsopt->mask); +            DEBUG_PRINTF ("classOffsets = %p\n", classOffsets); +            for (uint32_t i=0; i<clsopt->capacity; ++i) +            { +                const int32_t clsOffset = classOffsets[i].clsOffset; +                if (clsOffset & 1) +                    continue; // duplicate +                else if (clsOffset == invalidEntryOffset) +                    continue; // invalid offset +                 +                if (class_infos && idx < max_class_infos) +                { +                    class_infos[idx].isa = (Class)((uint8_t *)clsopt + clsOffset); +                    const char *name = class_getName (class_infos[idx].isa); +                    DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name); +                    // Hash the class name so we don't have to read it +                    const char *s = name; +                    uint32_t h = 5381; +                    for (unsigned char c = *s; c; c = *++s) +                        h = ((h << 5) + h) + c; +                    class_infos[idx].hash = h; +                } +                ++idx; +            } +             +            const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity]; +            const uint32_t duplicate_count = *duplicate_count_ptr; +            const objc_classheader_t *duplicateClassOffsets = (const objc_classheader_t *)(&duplicate_count_ptr[1]); +            DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count); +            DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets); +            for (uint32_t i=0; i<duplicate_count; ++i) +            { +                const int32_t clsOffset = duplicateClassOffsets[i].clsOffset; +                if (clsOffset & 1) +                    continue; // duplicate +                else if (clsOffset == invalidEntryOffset) +                    continue; // invalid offset +                 +                if (class_infos && idx < max_class_infos) +                { +                    class_infos[idx].isa = (Class)((uint8_t *)clsopt + clsOffset); +                    const char *name = class_getName (class_infos[idx].isa); +                    DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name); +                    // Hash the class name so we don't have to read it +                    const char *s = name; +                    uint32_t h = 5381; +                    for (unsigned char c = *s; c; c = *++s) +                        h = ((h << 5) + h) + c; +                    class_infos[idx].hash = h; +                } +                ++idx; +            } +        } +        DEBUG_PRINTF ("%u class_infos\n", idx); +        DEBUG_PRINTF ("done\n"); +    } +    return idx; +} + + +)"; + +static uint64_t +ExtractRuntimeGlobalSymbol (Process* process, +                            ConstString name, +                            const ModuleSP &module_sp, +                            Error& error, +                            bool read_value = true, +                            uint8_t byte_size = 0, +                            uint64_t default_value = LLDB_INVALID_ADDRESS, +                            SymbolType sym_type = lldb::eSymbolTypeData) +{ +    if (!process) +    { +        error.SetErrorString("no process"); +        return default_value; +    } +    if (!module_sp) +    { +        error.SetErrorString("no module"); +        return default_value; +    } +    if (!byte_size) +        byte_size = process->GetAddressByteSize(); +    const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(name, lldb::eSymbolTypeData); +    if (symbol && symbol->ValueIsAddress()) +    { +        lldb::addr_t symbol_load_addr = symbol->GetAddressRef().GetLoadAddress(&process->GetTarget()); +        if (symbol_load_addr != LLDB_INVALID_ADDRESS) +        { +            if (read_value) +                return process->ReadUnsignedIntegerFromMemory(symbol_load_addr, byte_size, default_value, error); +            else +                return symbol_load_addr; +        } +        else +        { +            error.SetErrorString("symbol address invalid"); +            return default_value; +        } +    } +    else +    { +        error.SetErrorString("no symbol"); +        return default_value; +    } +} + +AppleObjCRuntimeV2::AppleObjCRuntimeV2 (Process *process, +                                        const ModuleSP &objc_module_sp) : +    AppleObjCRuntime (process), +    m_get_class_info_code(), +    m_get_class_info_args (LLDB_INVALID_ADDRESS), +    m_get_class_info_args_mutex (Mutex::eMutexTypeNormal), +    m_get_shared_cache_class_info_code(), +    m_get_shared_cache_class_info_args (LLDB_INVALID_ADDRESS), +    m_get_shared_cache_class_info_args_mutex (Mutex::eMutexTypeNormal), +    m_decl_vendor_ap (), +    m_isa_hash_table_ptr (LLDB_INVALID_ADDRESS), +    m_hash_signature (), +    m_has_object_getClass (false), +    m_loaded_objc_opt (false), +    m_non_pointer_isa_cache_ap(NonPointerISACache::CreateInstance(*this,objc_module_sp)), +    m_tagged_pointer_vendor_ap(TaggedPointerVendorV2::CreateInstance(*this,objc_module_sp)), +    m_encoding_to_type_sp(), +    m_noclasses_warning_emitted(false) +{ +    static const ConstString g_gdb_object_getClass("gdb_object_getClass"); +    m_has_object_getClass = (objc_module_sp->FindFirstSymbolWithNameAndType(g_gdb_object_getClass, eSymbolTypeCode) != NULL); +} + +bool +AppleObjCRuntimeV2::GetDynamicTypeAndAddress (ValueObject &in_value, +                                              DynamicValueType use_dynamic,  +                                              TypeAndOrName &class_type_or_name,  +                                              Address &address, +                                              Value::ValueType &value_type) +{ +    // The Runtime is attached to a particular process, you shouldn't pass in a value from another process. +    assert (in_value.GetProcessSP().get() == m_process); +    assert (m_process != NULL); +     +    class_type_or_name.Clear(); +    value_type = Value::ValueType::eValueTypeScalar; + +    // Make sure we can have a dynamic value before starting... +    if (CouldHaveDynamicValue (in_value)) +    { +        // First job, pull out the address at 0 offset from the object  That will be the ISA pointer. +        ClassDescriptorSP objc_class_sp (GetNonKVOClassDescriptor (in_value)); +        if (objc_class_sp) +        { +            const addr_t object_ptr = in_value.GetPointerValue(); +            address.SetRawAddress(object_ptr); + +            ConstString class_name (objc_class_sp->GetClassName()); +            class_type_or_name.SetName(class_name); +            TypeSP type_sp (objc_class_sp->GetType()); +            if (type_sp) +                class_type_or_name.SetTypeSP (type_sp); +            else +            { +                type_sp = LookupInCompleteClassCache (class_name); +                if (type_sp) +                { +                    objc_class_sp->SetType (type_sp); +                    class_type_or_name.SetTypeSP (type_sp); +                } +                else +                { +                    // try to go for a CompilerType at least +                    DeclVendor* vendor = GetDeclVendor(); +                    if (vendor) +                    { +                        std::vector<clang::NamedDecl*> decls; +                        if (vendor->FindDecls(class_name, false, 1, decls) && decls.size()) +                            class_type_or_name.SetCompilerType(ClangASTContext::GetTypeForDecl(decls[0])); +                    } +                } +            } +        } +    }     +    return class_type_or_name.IsEmpty() == false; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +LanguageRuntime * +AppleObjCRuntimeV2::CreateInstance (Process *process, LanguageType language) +{ +    // FIXME: This should be a MacOS or iOS process, and we need to look for the OBJC section to make +    // sure we aren't using the V1 runtime. +    if (language == eLanguageTypeObjC) +    { +        ModuleSP objc_module_sp; +         +        if (AppleObjCRuntime::GetObjCVersion (process, objc_module_sp) == ObjCRuntimeVersions::eAppleObjC_V2) +            return new AppleObjCRuntimeV2 (process, objc_module_sp); +        else +            return NULL; +    } +    else +        return NULL; +} + +class CommandObjectObjC_ClassTable_Dump : public CommandObjectParsed +{ +public: +    CommandObjectObjC_ClassTable_Dump (CommandInterpreter &interpreter) : +    CommandObjectParsed (interpreter, +                         "dump", +                         "Dump information on Objective-C classes known to the current process.", +                         "language objc class-table dump", +                         eCommandRequiresProcess       | +                         eCommandProcessMustBeLaunched | +                         eCommandProcessMustBePaused   ) +    { +    } + +    ~CommandObjectObjC_ClassTable_Dump() override = default; + +protected: +    bool +    DoExecute(Args& command, CommandReturnObject &result) override +    { +        Process *process = m_exe_ctx.GetProcessPtr(); +        ObjCLanguageRuntime *objc_runtime = process->GetObjCLanguageRuntime(); +        if (objc_runtime) +        { +            auto iterators_pair = objc_runtime->GetDescriptorIteratorPair(); +            auto iterator = iterators_pair.first; +            for(; iterator != iterators_pair.second; iterator++) +            { +                result.GetOutputStream().Printf("isa = 0x%" PRIx64, iterator->first); +                if (iterator->second) +                { +                    result.GetOutputStream().Printf(" name = %s", iterator->second->GetClassName().AsCString("<unknown>")); +                    result.GetOutputStream().Printf(" instance size = %" PRIu64, iterator->second->GetInstanceSize()); +                    result.GetOutputStream().Printf(" num ivars = %" PRIuPTR, (uintptr_t)iterator->second->GetNumIVars()); +                    if (auto superclass = iterator->second->GetSuperclass()) +                    { +                        result.GetOutputStream().Printf(" superclass = %s", superclass->GetClassName().AsCString("<unknown>")); +                    } +                    result.GetOutputStream().Printf("\n"); +                } +                else +                { +                    result.GetOutputStream().Printf(" has no associated class.\n"); +                } +            } +            result.SetStatus(lldb::eReturnStatusSuccessFinishResult); +            return true; +        } +        else +        { +            result.AppendError("current process has no Objective-C runtime loaded"); +            result.SetStatus(lldb::eReturnStatusFailed); +            return false; +        } +    } +}; + +class CommandObjectMultiwordObjC_TaggedPointer_Info : public CommandObjectParsed +{ +public: +    CommandObjectMultiwordObjC_TaggedPointer_Info (CommandInterpreter &interpreter) : +    CommandObjectParsed (interpreter, +                         "info", +                         "Dump information on a tagged pointer.", +                         "language objc tagged-pointer info", +                         eCommandRequiresProcess       | +                         eCommandProcessMustBeLaunched | +                         eCommandProcessMustBePaused   ) +    { +        CommandArgumentEntry arg; +        CommandArgumentData index_arg; +         +        // Define the first (and only) variant of this arg. +        index_arg.arg_type = eArgTypeAddress; +        index_arg.arg_repetition = eArgRepeatPlus; +         +        // There is only one variant this argument could be; put it into the argument entry. +        arg.push_back (index_arg); +         +        // Push the data for the first argument into the m_arguments vector. +        m_arguments.push_back (arg); +    } + +    ~CommandObjectMultiwordObjC_TaggedPointer_Info() override = default; + +protected: +    bool +    DoExecute(Args& command, CommandReturnObject &result) override +    { +        if (command.GetArgumentCount() == 0) +        { +            result.AppendError("this command requires arguments"); +            result.SetStatus(lldb::eReturnStatusFailed); +            return false; +        } +         +        Process *process = m_exe_ctx.GetProcessPtr(); +        ExecutionContext exe_ctx(process); +        ObjCLanguageRuntime *objc_runtime = process->GetObjCLanguageRuntime(); +        if (objc_runtime) +        { +            ObjCLanguageRuntime::TaggedPointerVendor *tagged_ptr_vendor = objc_runtime->GetTaggedPointerVendor(); +            if (tagged_ptr_vendor) +            { +                for (size_t i = 0; +                     i < command.GetArgumentCount(); +                     i++) +                { +                    const char *arg_str = command.GetArgumentAtIndex(i); +                    if (!arg_str) +                        continue; +                    Error error; +                    lldb::addr_t arg_addr = Args::StringToAddress(&exe_ctx, arg_str, LLDB_INVALID_ADDRESS, &error); +                    if (arg_addr == 0 || arg_addr == LLDB_INVALID_ADDRESS || error.Fail()) +                        continue; +                    auto descriptor_sp = tagged_ptr_vendor->GetClassDescriptor(arg_addr); +                    if (!descriptor_sp) +                        continue; +                    uint64_t info_bits = 0; +                    uint64_t value_bits = 0; +                    uint64_t payload = 0; +                    if (descriptor_sp->GetTaggedPointerInfo(&info_bits, &value_bits, &payload)) +                    { +                        result.GetOutputStream().Printf("0x%" PRIx64 " is tagged.\n\tpayload = 0x%" PRIx64 "\n\tvalue = 0x%" PRIx64 "\n\tinfo bits = 0x%" PRIx64 "\n\tclass = %s\n", +                                                        (uint64_t)arg_addr, +                                                        payload, +                                                        value_bits, +                                                        info_bits, +                                                        descriptor_sp->GetClassName().AsCString("<unknown>")); +                    } +                    else +                    { +                        result.GetOutputStream().Printf("0x%" PRIx64 " is not tagged.\n", (uint64_t)arg_addr); +                    } +                } +            } +            else +            { +                result.AppendError("current process has no tagged pointer support"); +                result.SetStatus(lldb::eReturnStatusFailed); +                return false; +            } +            result.SetStatus(lldb::eReturnStatusSuccessFinishResult); +            return true; +        } +        else +        { +            result.AppendError("current process has no Objective-C runtime loaded"); +            result.SetStatus(lldb::eReturnStatusFailed); +            return false; +        } +    } +}; + +class CommandObjectMultiwordObjC_ClassTable : public CommandObjectMultiword +{ +public: +    CommandObjectMultiwordObjC_ClassTable (CommandInterpreter &interpreter) : +    CommandObjectMultiword (interpreter, +                            "class-table", +                            "A set of commands for operating on the Objective-C class table.", +                            "class-table <subcommand> [<subcommand-options>]") +    { +        LoadSubCommand ("dump",   CommandObjectSP (new CommandObjectObjC_ClassTable_Dump (interpreter))); +    } + +    ~CommandObjectMultiwordObjC_ClassTable() override = default; +}; + +class CommandObjectMultiwordObjC_TaggedPointer : public CommandObjectMultiword +{ +public: +     +    CommandObjectMultiwordObjC_TaggedPointer (CommandInterpreter &interpreter) : +    CommandObjectMultiword (interpreter, +                            "tagged-pointer", +                            "A set of commands for operating on Objective-C tagged pointers.", +                            "class-table <subcommand> [<subcommand-options>]") +    { +        LoadSubCommand ("info",   CommandObjectSP (new CommandObjectMultiwordObjC_TaggedPointer_Info (interpreter))); +    } + +    ~CommandObjectMultiwordObjC_TaggedPointer() override = default; +}; + +class CommandObjectMultiwordObjC : public CommandObjectMultiword +{ +public: +    CommandObjectMultiwordObjC (CommandInterpreter &interpreter) : +    CommandObjectMultiword (interpreter, +                            "objc", +                            "A set of commands for operating on the Objective-C Language Runtime.", +                            "objc <subcommand> [<subcommand-options>]") +    { +        LoadSubCommand ("class-table",   CommandObjectSP (new CommandObjectMultiwordObjC_ClassTable (interpreter))); +        LoadSubCommand ("tagged-pointer",   CommandObjectSP (new CommandObjectMultiwordObjC_TaggedPointer (interpreter))); +    } + +    ~CommandObjectMultiwordObjC() override = default; +}; + +void +AppleObjCRuntimeV2::Initialize() +{ +    PluginManager::RegisterPlugin (GetPluginNameStatic(), +                                   "Apple Objective C Language Runtime - Version 2", +                                   CreateInstance, +                                   [] (CommandInterpreter& interpreter) -> lldb::CommandObjectSP { +                                       return CommandObjectSP(new CommandObjectMultiwordObjC(interpreter)); +                                   }); +} + +void +AppleObjCRuntimeV2::Terminate() +{ +    PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +AppleObjCRuntimeV2::GetPluginNameStatic() +{ +    static ConstString g_name("apple-objc-v2"); +    return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +AppleObjCRuntimeV2::GetPluginName() +{ +    return GetPluginNameStatic(); +} + +uint32_t +AppleObjCRuntimeV2::GetPluginVersion() +{ +    return 1; +} + +BreakpointResolverSP +AppleObjCRuntimeV2::CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp) +{ +    BreakpointResolverSP resolver_sp; +     +    if (throw_bp) +        resolver_sp.reset (new BreakpointResolverName (bkpt, +                                                       "objc_exception_throw", +                                                       eFunctionNameTypeBase, +                                                       eLanguageTypeUnknown, +                                                       Breakpoint::Exact, +                                                       eLazyBoolNo)); +    // FIXME: We don't do catch breakpoints for ObjC yet. +    // Should there be some way for the runtime to specify what it can do in this regard? +    return resolver_sp; +} + +UtilityFunction * +AppleObjCRuntimeV2::CreateObjectChecker(const char *name) +{ +    char check_function_code[2048]; +     +    int len = 0; +    if (m_has_object_getClass) +    { +        len = ::snprintf (check_function_code,  +                          sizeof(check_function_code), +                          "extern \"C\" void *gdb_object_getClass(void *);                                          \n" +                          "extern \"C\"  int printf(const char *format, ...);                                       \n" +                          "extern \"C\" void                                                                        \n" +                          "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector)                                    \n" +                          "{                                                                                        \n" +                          "   if ($__lldb_arg_obj == (void *)0)                                                     \n" +                          "       return; // nil is ok                                                              \n"  +                          "   if (!gdb_object_getClass($__lldb_arg_obj))                                            \n" +                          "       *((volatile int *)0) = 'ocgc';                                                    \n" +                          "   else if ($__lldb_arg_selector != (void *)0)                                           \n" +                          "   {                                                                                     \n" +                          "        signed char responds = (signed char) [(id) $__lldb_arg_obj                       \n" +                          "                                                respondsToSelector:                      \n" +                          "                                       (struct objc_selector *) $__lldb_arg_selector];   \n" +                          "       if (responds == (signed char) 0)                                                  \n" +                          "           *((volatile int *)0) = 'ocgc';                                                \n" +                          "   }                                                                                     \n" +                          "}                                                                                        \n", +                          name); +    } +    else +    { +        len = ::snprintf (check_function_code,  +                          sizeof(check_function_code),  +                          "extern \"C\" void *gdb_class_getClass(void *);                                           \n" +                          "extern \"C\"  int printf(const char *format, ...);                                       \n" +                          "extern \"C\"  void                                                                       \n" +                          "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector)                                    \n" +                          "{                                                                                        \n" +                          "   if ($__lldb_arg_obj == (void *)0)                                                     \n" +                          "       return; // nil is ok                                                              \n"  +                          "    void **$isa_ptr = (void **)$__lldb_arg_obj;                                          \n" +                          "    if (*$isa_ptr == (void *)0 || !gdb_class_getClass(*$isa_ptr))                        \n" +                          "       *((volatile int *)0) = 'ocgc';                                                    \n" +                          "   else if ($__lldb_arg_selector != (void *)0)                                           \n" +                          "   {                                                                                     \n" +                          "        signed char responds = (signed char) [(id) $__lldb_arg_obj                       \n" +                          "                                                respondsToSelector:                      \n" +                          "                                        (struct objc_selector *) $__lldb_arg_selector];  \n" +                          "       if (responds == (signed char) 0)                                                  \n" +                          "           *((volatile int *)0) = 'ocgc';                                                \n" +                          "   }                                                                                     \n" +                          "}                                                                                        \n",  +                          name); +    } +     +    assert (len < (int)sizeof(check_function_code)); + +    Error error; +    return GetTargetRef().GetUtilityFunctionForLanguage(check_function_code, eLanguageTypeObjC, name, error); +} + +size_t +AppleObjCRuntimeV2::GetByteOffsetForIvar (CompilerType &parent_ast_type, const char *ivar_name) +{ +    uint32_t ivar_offset = LLDB_INVALID_IVAR_OFFSET; + +    const char *class_name = parent_ast_type.GetConstTypeName().AsCString(); +    if (class_name && class_name[0] && ivar_name && ivar_name[0]) +    { +        //---------------------------------------------------------------------- +        // Make the objective C V2 mangled name for the ivar offset from the +        // class name and ivar name +        //---------------------------------------------------------------------- +        std::string buffer("OBJC_IVAR_$_"); +        buffer.append (class_name); +        buffer.push_back ('.'); +        buffer.append (ivar_name); +        ConstString ivar_const_str (buffer.c_str()); +         +        //---------------------------------------------------------------------- +        // Try to get the ivar offset address from the symbol table first using +        // the name we created above +        //---------------------------------------------------------------------- +        SymbolContextList sc_list; +        Target &target = m_process->GetTarget(); +        target.GetImages().FindSymbolsWithNameAndType(ivar_const_str, eSymbolTypeObjCIVar, sc_list); + +        addr_t ivar_offset_address = LLDB_INVALID_ADDRESS; + +        Error error; +        SymbolContext ivar_offset_symbol; +        if (sc_list.GetSize() == 1 && sc_list.GetContextAtIndex(0, ivar_offset_symbol)) +        { +            if (ivar_offset_symbol.symbol) +                ivar_offset_address = ivar_offset_symbol.symbol->GetLoadAddress (&target); +        } + +        //---------------------------------------------------------------------- +        // If we didn't get the ivar offset address from the symbol table, fall +        // back to getting it from the runtime +        //---------------------------------------------------------------------- +        if (ivar_offset_address == LLDB_INVALID_ADDRESS) +            ivar_offset_address = LookupRuntimeSymbol(ivar_const_str); + +        if (ivar_offset_address != LLDB_INVALID_ADDRESS) +            ivar_offset = m_process->ReadUnsignedIntegerFromMemory (ivar_offset_address, +                                                                    4, +                                                                    LLDB_INVALID_IVAR_OFFSET, +                                                                    error); +    } +    return ivar_offset; +} + +// tagged pointers are special not-a-real-pointer values that contain both type and value information +// this routine attempts to check with as little computational effort as possible whether something +// could possibly be a tagged pointer - false positives are possible but false negatives shouldn't +bool +AppleObjCRuntimeV2::IsTaggedPointer(addr_t ptr) +{ +    if (!m_tagged_pointer_vendor_ap) +        return false; +    return m_tagged_pointer_vendor_ap->IsPossibleTaggedPointer(ptr); +} + +class RemoteNXMapTable +{ +public: +    RemoteNXMapTable () : +        m_count (0), +        m_num_buckets_minus_one (0), +        m_buckets_ptr (LLDB_INVALID_ADDRESS), +        m_process (NULL), +        m_end_iterator (*this, -1), +        m_load_addr (LLDB_INVALID_ADDRESS), +        m_map_pair_size (0), +        m_invalid_key (0) +    { +    } +     +    void +    Dump () +    { +        printf ("RemoteNXMapTable.m_load_addr = 0x%" PRIx64 "\n", m_load_addr); +        printf ("RemoteNXMapTable.m_count = %u\n", m_count); +        printf ("RemoteNXMapTable.m_num_buckets_minus_one = %u\n", m_num_buckets_minus_one); +        printf ("RemoteNXMapTable.m_buckets_ptr = 0x%" PRIX64 "\n", m_buckets_ptr); +    } +     +    bool +    ParseHeader (Process* process, lldb::addr_t load_addr) +    { +        m_process = process; +        m_load_addr = load_addr; +        m_map_pair_size = m_process->GetAddressByteSize() * 2; +        m_invalid_key = m_process->GetAddressByteSize() == 8 ? UINT64_MAX : UINT32_MAX; +        Error err; +         +        // This currently holds true for all platforms we support, but we might +        // need to change this to use get the actually byte size of "unsigned" +        // from the target AST... +        const uint32_t unsigned_byte_size = sizeof(uint32_t); +        // Skip the prototype as we don't need it (const struct +NXMapTablePrototype *prototype) +         +        bool success = true; +        if (load_addr == LLDB_INVALID_ADDRESS) +            success = false; +        else +        { +            lldb::addr_t cursor = load_addr + m_process->GetAddressByteSize(); +                     +            // unsigned count; +            m_count = m_process->ReadUnsignedIntegerFromMemory(cursor, unsigned_byte_size, 0, err); +            if (m_count) +            { +                cursor += unsigned_byte_size; +             +                // unsigned nbBucketsMinusOne; +                m_num_buckets_minus_one = m_process->ReadUnsignedIntegerFromMemory(cursor, unsigned_byte_size, 0, err); +                cursor += unsigned_byte_size; +             +                // void *buckets; +                m_buckets_ptr = m_process->ReadPointerFromMemory(cursor, err); +                 +                success = m_count > 0 && m_buckets_ptr != LLDB_INVALID_ADDRESS; +            } +        } +         +        if (!success) +        { +            m_count = 0; +            m_num_buckets_minus_one = 0; +            m_buckets_ptr = LLDB_INVALID_ADDRESS; +        } +        return success; +    } +     +    // const_iterator mimics NXMapState and its code comes from NXInitMapState and NXNextMapState. +    typedef std::pair<ConstString, ObjCLanguageRuntime::ObjCISA> element; + +    friend class const_iterator; +    class const_iterator +    { +    public: +        const_iterator (RemoteNXMapTable &parent, int index) : m_parent(parent), m_index(index) +        { +            AdvanceToValidIndex(); +        } +         +        const_iterator (const const_iterator &rhs) : m_parent(rhs.m_parent), m_index(rhs.m_index) +        { +            // AdvanceToValidIndex() has been called by rhs already. +        } +         +        const_iterator &operator=(const const_iterator &rhs) +        { +            // AdvanceToValidIndex() has been called by rhs already. +            assert (&m_parent == &rhs.m_parent); +            m_index = rhs.m_index; +            return *this; +        } +         +        bool operator==(const const_iterator &rhs) const +        { +            if (&m_parent != &rhs.m_parent) +                return false; +            if (m_index != rhs.m_index) +                return false; +             +            return true; +        } +         +        bool operator!=(const const_iterator &rhs) const +        { +            return !(operator==(rhs)); +        } +         +        const_iterator &operator++() +        { +            AdvanceToValidIndex(); +            return *this; +        } +         +        const element operator*() const +        { +            if (m_index == -1) +            { +                // TODO find a way to make this an error, but not an assert +                return element(); +            } +          +            lldb::addr_t pairs_ptr = m_parent.m_buckets_ptr; +            size_t map_pair_size = m_parent.m_map_pair_size; +            lldb::addr_t pair_ptr = pairs_ptr + (m_index * map_pair_size); +             +            Error err; +             +            lldb::addr_t key = m_parent.m_process->ReadPointerFromMemory(pair_ptr, err); +            if (!err.Success()) +                return element(); +            lldb::addr_t value = m_parent.m_process->ReadPointerFromMemory(pair_ptr + m_parent.m_process->GetAddressByteSize(), err); +            if (!err.Success()) +                return element(); +             +            std::string key_string; +             +            m_parent.m_process->ReadCStringFromMemory(key, key_string, err); +            if (!err.Success()) +                return element(); +             +            return element(ConstString(key_string.c_str()), (ObjCLanguageRuntime::ObjCISA)value); +        } + +    private: +        void AdvanceToValidIndex () +        { +            if (m_index == -1) +                return; +             +            const lldb::addr_t pairs_ptr = m_parent.m_buckets_ptr; +            const size_t map_pair_size = m_parent.m_map_pair_size; +            const lldb::addr_t invalid_key = m_parent.m_invalid_key; +            Error err; + +            while (m_index--) +            { +                lldb::addr_t pair_ptr = pairs_ptr + (m_index * map_pair_size); +                lldb::addr_t key = m_parent.m_process->ReadPointerFromMemory(pair_ptr, err); +                 +                if (!err.Success()) +                { +                    m_index = -1; +                    return; +                } +                 +                if (key != invalid_key) +                    return; +            } +        } +        RemoteNXMapTable   &m_parent; +        int                 m_index; +    }; +     +    const_iterator begin () +    { +        return const_iterator(*this, m_num_buckets_minus_one + 1); +    } +     +    const_iterator end () +    { +        return m_end_iterator; +    } +     +    uint32_t +    GetCount () const +    { +        return m_count; +    } +     +    uint32_t +    GetBucketCount () const +    { +        return m_num_buckets_minus_one; +    } +     +    lldb::addr_t +    GetBucketDataPointer () const +    { +        return m_buckets_ptr; +    } +     +    lldb::addr_t +    GetTableLoadAddress() const +    { +        return m_load_addr; +    } + +private: +    // contents of _NXMapTable struct +    uint32_t m_count; +    uint32_t m_num_buckets_minus_one; +    lldb::addr_t m_buckets_ptr; +    lldb_private::Process *m_process; +    const_iterator m_end_iterator; +    lldb::addr_t m_load_addr; +    size_t m_map_pair_size; +    lldb::addr_t m_invalid_key; +}; + +AppleObjCRuntimeV2::HashTableSignature::HashTableSignature() : +    m_count (0), +    m_num_buckets (0), +    m_buckets_ptr (0) +{ +} + +void +AppleObjCRuntimeV2::HashTableSignature::UpdateSignature (const RemoteNXMapTable &hash_table) +{ +    m_count = hash_table.GetCount(); +    m_num_buckets = hash_table.GetBucketCount(); +    m_buckets_ptr = hash_table.GetBucketDataPointer(); +} + +bool +AppleObjCRuntimeV2::HashTableSignature::NeedsUpdate (Process *process, AppleObjCRuntimeV2 *runtime, RemoteNXMapTable &hash_table) +{ +    if (!hash_table.ParseHeader(process, runtime->GetISAHashTablePointer ())) +    { +        return false; // Failed to parse the header, no need to update anything +    } + +    // Check with out current signature and return true if the count, +    // number of buckets or the hash table address changes. +    if (m_count == hash_table.GetCount() && +        m_num_buckets == hash_table.GetBucketCount() && +        m_buckets_ptr == hash_table.GetBucketDataPointer()) +    { +        // Hash table hasn't changed +        return false; +    } +    // Hash table data has changed, we need to update +    return true; +} + +ObjCLanguageRuntime::ClassDescriptorSP +AppleObjCRuntimeV2::GetClassDescriptorFromISA (ObjCISA isa) +{ +    ObjCLanguageRuntime::ClassDescriptorSP class_descriptor_sp; +    if (m_non_pointer_isa_cache_ap.get()) +        class_descriptor_sp = m_non_pointer_isa_cache_ap->GetClassDescriptor(isa); +    if (!class_descriptor_sp) +        class_descriptor_sp = ObjCLanguageRuntime::GetClassDescriptorFromISA(isa); +    return class_descriptor_sp; +} + +ObjCLanguageRuntime::ClassDescriptorSP +AppleObjCRuntimeV2::GetClassDescriptor (ValueObject& valobj) +{ +    ClassDescriptorSP objc_class_sp; +    if (valobj.IsBaseClass()) +    { +        ValueObject *parent = valobj.GetParent(); +        // if I am my own parent, bail out of here fast.. +        if (parent && parent != &valobj) +        { +            ClassDescriptorSP parent_descriptor_sp = GetClassDescriptor(*parent); +            if (parent_descriptor_sp) +                return parent_descriptor_sp->GetSuperclass(); +        } +        return nullptr; +    } +    // if we get an invalid VO (which might still happen when playing around +    // with pointers returned by the expression parser, don't consider this +    // a valid ObjC object) +    if (valobj.GetCompilerType().IsValid()) +    { +        addr_t isa_pointer = valobj.GetPointerValue(); +         +        // tagged pointer +        if (IsTaggedPointer(isa_pointer)) +        { +            return m_tagged_pointer_vendor_ap->GetClassDescriptor(isa_pointer); +        } +        else +        { +            ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); +             +            Process *process = exe_ctx.GetProcessPtr(); +            if (process) +            { +                Error error; +                ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error); +                if (isa != LLDB_INVALID_ADDRESS) +                { +                    objc_class_sp = GetClassDescriptorFromISA (isa); +                    if (isa && !objc_class_sp) +                    { +                        Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); +                        if (log) +                            log->Printf("0x%" PRIx64 ": AppleObjCRuntimeV2::GetClassDescriptor() ISA was not in class descriptor cache 0x%" PRIx64, +                                        isa_pointer, +                                        isa); +                    } +                } +            } +        } +    } +    return objc_class_sp; +} + +lldb::addr_t +AppleObjCRuntimeV2::GetISAHashTablePointer () +{ +    if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) +    { +        Process *process = GetProcess(); + +        ModuleSP objc_module_sp(GetObjCModule()); +         +        if (!objc_module_sp) +            return LLDB_INVALID_ADDRESS; + +        static ConstString g_gdb_objc_realized_classes("gdb_objc_realized_classes"); +         +        const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(g_gdb_objc_realized_classes, lldb::eSymbolTypeAny); +        if (symbol) +        { +            lldb::addr_t gdb_objc_realized_classes_ptr = symbol->GetLoadAddress(&process->GetTarget()); +             +            if (gdb_objc_realized_classes_ptr != LLDB_INVALID_ADDRESS) +            { +                Error error; +                m_isa_hash_table_ptr = process->ReadPointerFromMemory(gdb_objc_realized_classes_ptr, error); +            } +        } +    } +    return m_isa_hash_table_ptr; +} + +bool +AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table) +{ +    Process *process = GetProcess(); +     +    if (process == NULL) +        return false; +     +    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); +     +    ExecutionContext exe_ctx; +     +    ThreadSP thread_sp = process->GetThreadList().GetSelectedThread(); +     +    if (!thread_sp) +        return false; +     +    thread_sp->CalculateExecutionContext(exe_ctx); +    ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext(); +     +    if (!ast) +        return false; +     +    Address function_address; +     +    StreamString errors; +     +    const uint32_t addr_size = process->GetAddressByteSize(); +     +    Error err; +     +    // Read the total number of classes from the hash table +    const uint32_t num_classes = hash_table.GetCount(); +    if (num_classes == 0) +    { +        if (log) +            log->Printf ("No dynamic classes found in gdb_objc_realized_classes."); +        return false; +    } +     +    // Make some types for our arguments +    CompilerType clang_uint32_t_type = ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32); +    CompilerType clang_void_pointer_type = ast->GetBasicType(eBasicTypeVoid).GetPointerType(); +     +    ValueList arguments; +    FunctionCaller *get_class_info_function = nullptr; + +    if (!m_get_class_info_code.get()) +    { +        Error error; +        m_get_class_info_code.reset (GetTargetRef().GetUtilityFunctionForLanguage (g_get_dynamic_class_info_body, +                                                                                   eLanguageTypeObjC, +                                                                                   g_get_dynamic_class_info_name, +                                                                                   error)); +        if (error.Fail()) +        { +            if (log) +                log->Printf ("Failed to get Utility Function for implementation lookup: %s", error.AsCString()); +            m_get_class_info_code.reset(); +        } +        else +        { +            errors.Clear(); +             +            if (!m_get_class_info_code->Install(errors, exe_ctx)) +            { +                if (log) +                    log->Printf ("Failed to install implementation lookup: %s.", errors.GetData()); +                m_get_class_info_code.reset(); +            } +        } +        if (!m_get_class_info_code.get()) +            return false; +         +        // Next make the runner function for our implementation utility function. +        Value value; +        value.SetValueType (Value::eValueTypeScalar); +        value.SetCompilerType (clang_void_pointer_type); +        arguments.PushValue (value); +        arguments.PushValue (value); +         +        value.SetValueType (Value::eValueTypeScalar); +        value.SetCompilerType (clang_uint32_t_type); +        arguments.PushValue (value); +         +        get_class_info_function = m_get_class_info_code->MakeFunctionCaller(clang_uint32_t_type, +                                                                            arguments, +                                                                            error); +         +        if (error.Fail()) +        { +            if (log) +                log->Printf("Failed to make function caller for implementation lookup: %s.", error.AsCString()); +            return false; +        } +    } +    else +    { +        get_class_info_function = m_get_class_info_code->GetFunctionCaller(); +        if (!get_class_info_function) +        { +            if (log) +                log->Printf ("Failed to get implementation lookup function caller: %s.", errors.GetData()); +            return false; +        } +        arguments = get_class_info_function->GetArgumentValues(); +    } + +    errors.Clear(); +     +    const uint32_t class_info_byte_size = addr_size + 4; +    const uint32_t class_infos_byte_size = num_classes * class_info_byte_size; +    lldb::addr_t class_infos_addr = process->AllocateMemory(class_infos_byte_size, +                                                            ePermissionsReadable | ePermissionsWritable, +                                                            err); +     +    if (class_infos_addr == LLDB_INVALID_ADDRESS) +        return false; +     +    Mutex::Locker locker(m_get_class_info_args_mutex); +     +    // Fill in our function argument values +    arguments.GetValueAtIndex(0)->GetScalar() = hash_table.GetTableLoadAddress(); +    arguments.GetValueAtIndex(1)->GetScalar() = class_infos_addr; +    arguments.GetValueAtIndex(2)->GetScalar() = class_infos_byte_size; +     +    bool success = false; +     +    errors.Clear(); +     +    // Write our function arguments into the process so we can run our function +    if (get_class_info_function->WriteFunctionArguments (exe_ctx, +                                                           m_get_class_info_args, +                                                           arguments, +                                                           errors)) +    { +        EvaluateExpressionOptions options; +        options.SetUnwindOnError(true); +        options.SetTryAllThreads(false); +        options.SetStopOthers(true); +        options.SetIgnoreBreakpoints(true); +        options.SetTimeoutUsec(UTILITY_FUNCTION_TIMEOUT_USEC); +         +        Value return_value; +        return_value.SetValueType (Value::eValueTypeScalar); +        //return_value.SetContext (Value::eContextTypeClangType, clang_uint32_t_type); +        return_value.SetCompilerType (clang_uint32_t_type); +        return_value.GetScalar() = 0; +         +        errors.Clear(); +         +        // Run the function +        ExpressionResults results = get_class_info_function->ExecuteFunction (exe_ctx, +                                                                              &m_get_class_info_args, +                                                                              options, +                                                                              errors, +                                                                              return_value); +         +        if (results == eExpressionCompleted) +        { +            // The result is the number of ClassInfo structures that were filled in +            uint32_t num_class_infos = return_value.GetScalar().ULong(); +            if (log) +                log->Printf("Discovered %u ObjC classes\n",num_class_infos); +            if (num_class_infos > 0) +            { +                // Read the ClassInfo structures +                DataBufferHeap buffer (num_class_infos * class_info_byte_size, 0); +                if (process->ReadMemory(class_infos_addr, buffer.GetBytes(), buffer.GetByteSize(), err) == buffer.GetByteSize()) +                { +                    DataExtractor class_infos_data (buffer.GetBytes(), +                                                    buffer.GetByteSize(), +                                                    process->GetByteOrder(), +                                                    addr_size); +                    ParseClassInfoArray (class_infos_data, num_class_infos); +                } +            } +            success = true; +        } +        else +        { +            if (log) +                log->Printf("Error evaluating our find class name function: %s.\n", errors.GetData()); +        } +    } +    else +    { +        if (log) +            log->Printf ("Error writing function arguments: \"%s\".", errors.GetData()); +    } +     +    // Deallocate the memory we allocated for the ClassInfo array +    process->DeallocateMemory(class_infos_addr); +     +    return success; +} + +uint32_t +AppleObjCRuntimeV2::ParseClassInfoArray (const DataExtractor &data, uint32_t num_class_infos) +{ +    // Parses an array of "num_class_infos" packed ClassInfo structures: +    // +    //    struct ClassInfo +    //    { +    //        Class isa; +    //        uint32_t hash; +    //    } __attribute__((__packed__)); + +    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); +     +    uint32_t num_parsed = 0; + +    // Iterate through all ClassInfo structures +    lldb::offset_t offset = 0; +    for (uint32_t i=0; i<num_class_infos; ++i) +    { +        ObjCISA isa = data.GetPointer(&offset); +         +        if (isa == 0) +        { +            if (log) +                log->Printf("AppleObjCRuntimeV2 found NULL isa, ignoring this class info"); +            continue; +        } +        // Check if we already know about this ISA, if we do, the info will +        // never change, so we can just skip it. +        if (ISAIsCached(isa)) +        { +            offset += 4; +        } +        else +        { +            // Read the 32 bit hash for the class name +            const uint32_t name_hash = data.GetU32(&offset); +            ClassDescriptorSP descriptor_sp (new ClassDescriptorV2(*this, isa, NULL)); +            AddClass (isa, descriptor_sp, name_hash); +            num_parsed++; +            if (log && log->GetVerbose()) +                log->Printf("AppleObjCRuntimeV2 added isa=0x%" PRIx64 ", hash=0x%8.8x, name=%s", isa, name_hash,descriptor_sp->GetClassName().AsCString("<unknown>")); +        } +    } +    return num_parsed; +} + +AppleObjCRuntimeV2::DescriptorMapUpdateResult +AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache() +{ +    Process *process = GetProcess(); +     +    if (process == NULL) +        return DescriptorMapUpdateResult::Fail(); +     +    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); +     +    ExecutionContext exe_ctx; +     +    ThreadSP thread_sp = process->GetThreadList().GetSelectedThread(); +     +    if (!thread_sp) +        return DescriptorMapUpdateResult::Fail(); +     +    thread_sp->CalculateExecutionContext(exe_ctx); +    ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext(); +     +    if (!ast) +        return DescriptorMapUpdateResult::Fail(); +     +    Address function_address; +     +    StreamString errors; +     +    const uint32_t addr_size = process->GetAddressByteSize(); +     +    Error err; +     +    const lldb::addr_t objc_opt_ptr = GetSharedCacheReadOnlyAddress(); +     +    if (objc_opt_ptr == LLDB_INVALID_ADDRESS) +        return DescriptorMapUpdateResult::Fail(); +     +    // Read the total number of classes from the hash table +    const uint32_t num_classes = 128*1024; +    if (num_classes == 0) +    { +        if (log) +            log->Printf ("No dynamic classes found in gdb_objc_realized_classes_addr."); +        return DescriptorMapUpdateResult::Fail(); +    } +     +    // Make some types for our arguments +    CompilerType clang_uint32_t_type = ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32); +    CompilerType clang_void_pointer_type = ast->GetBasicType(eBasicTypeVoid).GetPointerType(); +     +    ValueList arguments; +    FunctionCaller *get_shared_cache_class_info_function = nullptr; +     +    if (!m_get_shared_cache_class_info_code.get()) +    { +        Error error; +        m_get_shared_cache_class_info_code.reset (GetTargetRef().GetUtilityFunctionForLanguage (g_get_shared_cache_class_info_body, +                                                                                                eLanguageTypeObjC, +                                                                                                g_get_shared_cache_class_info_name, +                                                                                                error)); +        if (error.Fail()) +        { +            if (log) +                log->Printf ("Failed to get Utility function for implementation lookup: %s.", error.AsCString()); +            m_get_shared_cache_class_info_code.reset(); +        } +        else +        { +            errors.Clear(); +             +            if (!m_get_shared_cache_class_info_code->Install(errors, exe_ctx)) +            { +                if (log) +                    log->Printf ("Failed to install implementation lookup: %s.", errors.GetData()); +                m_get_shared_cache_class_info_code.reset(); +            } +        } +         +        if (!m_get_shared_cache_class_info_code.get()) +            return DescriptorMapUpdateResult::Fail(); +     +        // Next make the function caller for our implementation utility function. +        Value value; +        value.SetValueType (Value::eValueTypeScalar); +        //value.SetContext (Value::eContextTypeClangType, clang_void_pointer_type); +        value.SetCompilerType (clang_void_pointer_type); +        arguments.PushValue (value); +        arguments.PushValue (value); +         +        value.SetValueType (Value::eValueTypeScalar); +        //value.SetContext (Value::eContextTypeClangType, clang_uint32_t_type); +        value.SetCompilerType (clang_uint32_t_type); +        arguments.PushValue (value); +         +        get_shared_cache_class_info_function = m_get_shared_cache_class_info_code->MakeFunctionCaller(clang_uint32_t_type, +                                                                                                      arguments, +                                                                                                      error); +         +        if (get_shared_cache_class_info_function == nullptr) +            return DescriptorMapUpdateResult::Fail(); +     +    } +    else +    { +        get_shared_cache_class_info_function = m_get_shared_cache_class_info_code->GetFunctionCaller(); +        if (get_shared_cache_class_info_function == nullptr) +            return DescriptorMapUpdateResult::Fail(); +        arguments = get_shared_cache_class_info_function->GetArgumentValues(); +    } +     +    errors.Clear(); +     +    const uint32_t class_info_byte_size = addr_size + 4; +    const uint32_t class_infos_byte_size = num_classes * class_info_byte_size; +    lldb::addr_t class_infos_addr = process->AllocateMemory (class_infos_byte_size, +                                                             ePermissionsReadable | ePermissionsWritable, +                                                             err); +     +    if (class_infos_addr == LLDB_INVALID_ADDRESS) +        return DescriptorMapUpdateResult::Fail(); +     +    Mutex::Locker locker(m_get_shared_cache_class_info_args_mutex); +     +    // Fill in our function argument values +    arguments.GetValueAtIndex(0)->GetScalar() = objc_opt_ptr; +    arguments.GetValueAtIndex(1)->GetScalar() = class_infos_addr; +    arguments.GetValueAtIndex(2)->GetScalar() = class_infos_byte_size; +     +    bool success = false; +    bool any_found = false; +     +    errors.Clear(); +     +    // Write our function arguments into the process so we can run our function +    if (get_shared_cache_class_info_function->WriteFunctionArguments (exe_ctx, +                                                                      m_get_shared_cache_class_info_args, +                                                                      arguments, +                                                                      errors)) +    { +        EvaluateExpressionOptions options; +        options.SetUnwindOnError(true); +        options.SetTryAllThreads(false); +        options.SetStopOthers(true); +        options.SetIgnoreBreakpoints(true); +        options.SetTimeoutUsec(UTILITY_FUNCTION_TIMEOUT_USEC); +         +        Value return_value; +        return_value.SetValueType (Value::eValueTypeScalar); +        //return_value.SetContext (Value::eContextTypeClangType, clang_uint32_t_type); +        return_value.SetCompilerType (clang_uint32_t_type); +        return_value.GetScalar() = 0; +         +        errors.Clear(); +         +        // Run the function +        ExpressionResults results = get_shared_cache_class_info_function->ExecuteFunction (exe_ctx, +                                                                                           &m_get_shared_cache_class_info_args, +                                                                                           options, +                                                                                           errors, +                                                                                           return_value); +         +        if (results == eExpressionCompleted) +        { +            // The result is the number of ClassInfo structures that were filled in +            uint32_t num_class_infos = return_value.GetScalar().ULong(); +            if (log) +                log->Printf("Discovered %u ObjC classes in shared cache\n",num_class_infos); +#ifdef LLDB_CONFIGURATION_DEBUG +            assert (num_class_infos <= num_classes); +#endif +            if (num_class_infos > 0) +            { +                if (num_class_infos > num_classes) +                { +                    num_class_infos = num_classes; +                     +                    success = false; +                } +                else +                { +                    success = true; +                } +                 +                // Read the ClassInfo structures +                DataBufferHeap buffer (num_class_infos * class_info_byte_size, 0); +                if (process->ReadMemory(class_infos_addr, +                                        buffer.GetBytes(), +                                        buffer.GetByteSize(), +                                        err) == buffer.GetByteSize()) +                { +                    DataExtractor class_infos_data (buffer.GetBytes(), +                                                    buffer.GetByteSize(), +                                                    process->GetByteOrder(), +                                                    addr_size); + +                    any_found = (ParseClassInfoArray (class_infos_data, num_class_infos) > 0); +                } +            } +            else +            { +                success = true; +            } +        } +        else +        { +            if (log) +                log->Printf("Error evaluating our find class name function: %s.\n", errors.GetData()); +        } +    } +    else +    { +        if (log) +            log->Printf ("Error writing function arguments: \"%s\".", errors.GetData()); +    } +     +    // Deallocate the memory we allocated for the ClassInfo array +    process->DeallocateMemory(class_infos_addr); +     +    return DescriptorMapUpdateResult(success, any_found); +} + +bool +AppleObjCRuntimeV2::UpdateISAToDescriptorMapFromMemory (RemoteNXMapTable &hash_table) +{ +    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); +     +    Process *process = GetProcess(); + +    if (process == NULL) +        return false; +     +    uint32_t num_map_table_isas = 0; +     +    ModuleSP objc_module_sp(GetObjCModule()); +     +    if (objc_module_sp) +    { +        for (RemoteNXMapTable::element elt : hash_table) +        { +            ++num_map_table_isas; +             +            if (ISAIsCached(elt.second)) +                continue; +             +            ClassDescriptorSP descriptor_sp = ClassDescriptorSP(new ClassDescriptorV2(*this, elt.second, elt.first.AsCString())); +             +            if (log && log->GetVerbose()) +                log->Printf("AppleObjCRuntimeV2 added (ObjCISA)0x%" PRIx64 " (%s) from dynamic table to isa->descriptor cache", elt.second, elt.first.AsCString()); +             +            AddClass (elt.second, descriptor_sp, elt.first.AsCString()); +        } +    } +     +    return num_map_table_isas > 0; +} + +lldb::addr_t +AppleObjCRuntimeV2::GetSharedCacheReadOnlyAddress() +{ +    Process *process = GetProcess(); +     +    if (process) +    { +        ModuleSP objc_module_sp(GetObjCModule()); +         +        if (objc_module_sp) +        { +            ObjectFile *objc_object = objc_module_sp->GetObjectFile(); +             +            if (objc_object) +            { +                SectionList *section_list = objc_module_sp->GetSectionList(); +                 +                if (section_list) +                { +                    SectionSP text_segment_sp (section_list->FindSectionByName(ConstString("__TEXT"))); +                     +                    if (text_segment_sp) +                    { +                        SectionSP objc_opt_section_sp (text_segment_sp->GetChildren().FindSectionByName(ConstString("__objc_opt_ro"))); +                         +                        if (objc_opt_section_sp) +                        { +                            return objc_opt_section_sp->GetLoadBaseAddress(&process->GetTarget()); +                        } +                    } +                } +            } +        } +    } +    return LLDB_INVALID_ADDRESS; +} + +void +AppleObjCRuntimeV2::UpdateISAToDescriptorMapIfNeeded() +{ +    Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); +     +    // Else we need to check with our process to see when the map was updated. +    Process *process = GetProcess(); + +    if (process) +    { +        RemoteNXMapTable hash_table; +         +        // Update the process stop ID that indicates the last time we updated the +        // map, whether it was successful or not. +        m_isa_to_descriptor_stop_id = process->GetStopID(); +         +        if (!m_hash_signature.NeedsUpdate(process, this, hash_table)) +            return; +         +        m_hash_signature.UpdateSignature (hash_table); + +        // Grab the dynamically loaded objc classes from the hash table in memory +        UpdateISAToDescriptorMapDynamic(hash_table); + +        // Now get the objc classes that are baked into the Objective C runtime +        // in the shared cache, but only once per process as this data never +        // changes +        if (!m_loaded_objc_opt) +        { +            DescriptorMapUpdateResult shared_cache_update_result = UpdateISAToDescriptorMapSharedCache(); +            if (!shared_cache_update_result.any_found) +                WarnIfNoClassesCached (); +            else +                m_loaded_objc_opt = true; +        } +    } +    else +    { +        m_isa_to_descriptor_stop_id = UINT32_MAX; +    } +} + +void +AppleObjCRuntimeV2::WarnIfNoClassesCached () +{ +    if (m_noclasses_warning_emitted) +        return; + +#if defined(__APPLE__) +    if (m_process && +        m_process->GetTarget().GetPlatform() && +        m_process->GetTarget().GetPlatform()->GetPluginName() == PlatformiOSSimulator::GetPluginNameStatic()) +    { +        // the iOS simulator does not have the objc_opt_ro class table +        // so don't actually complain to the user +        m_noclasses_warning_emitted = true; +        return; +    } +#endif + +    Debugger &debugger(GetProcess()->GetTarget().GetDebugger()); +     +    if (debugger.GetAsyncOutputStream()) +    { +        debugger.GetAsyncOutputStream()->PutCString("warning: could not load any Objective-C class information from the dyld shared cache. This will significantly reduce the quality of type information available.\n"); +        m_noclasses_warning_emitted = true; +    } +} + +// TODO: should we have a transparent_kvo parameter here to say if we +// want to replace the KVO swizzled class with the actual user-level type? +ConstString +AppleObjCRuntimeV2::GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa) +{ +    if (isa == g_objc_Tagged_ISA) +    { +        static const ConstString g_objc_tagged_isa_name ("_lldb_Tagged_ObjC_ISA"); +        return g_objc_tagged_isa_name; +    } +    if (isa == g_objc_Tagged_ISA_NSAtom) +    { +        static const ConstString g_objc_tagged_isa_nsatom_name ("NSAtom"); +        return g_objc_tagged_isa_nsatom_name; +    } +    if (isa == g_objc_Tagged_ISA_NSNumber) +    { +        static const ConstString g_objc_tagged_isa_nsnumber_name ("NSNumber"); +        return g_objc_tagged_isa_nsnumber_name; +    } +    if (isa == g_objc_Tagged_ISA_NSDateTS) +    { +        static const ConstString g_objc_tagged_isa_nsdatets_name ("NSDateTS"); +        return g_objc_tagged_isa_nsdatets_name; +    } +    if (isa == g_objc_Tagged_ISA_NSManagedObject) +    { +        static const ConstString g_objc_tagged_isa_nsmanagedobject_name ("NSManagedObject"); +        return g_objc_tagged_isa_nsmanagedobject_name; +    } +    if (isa == g_objc_Tagged_ISA_NSDate) +    { +        static const ConstString g_objc_tagged_isa_nsdate_name ("NSDate"); +        return g_objc_tagged_isa_nsdate_name; +    } +    return ObjCLanguageRuntime::GetActualTypeName(isa); +} + +DeclVendor * +AppleObjCRuntimeV2::GetDeclVendor() +{ +    if (!m_decl_vendor_ap.get()) +        m_decl_vendor_ap.reset(new AppleObjCDeclVendor(*this)); +     +    return m_decl_vendor_ap.get(); +} + +lldb::addr_t +AppleObjCRuntimeV2::LookupRuntimeSymbol (const ConstString &name) +{ +    lldb::addr_t ret = LLDB_INVALID_ADDRESS; + +    const char *name_cstr = name.AsCString();     +     +    if (name_cstr) +    { +        llvm::StringRef name_strref(name_cstr); +         +        static const llvm::StringRef ivar_prefix("OBJC_IVAR_$_"); +        static const llvm::StringRef class_prefix("OBJC_CLASS_$_"); +         +        if (name_strref.startswith(ivar_prefix)) +        { +            llvm::StringRef ivar_skipped_prefix = name_strref.substr(ivar_prefix.size()); +            std::pair<llvm::StringRef, llvm::StringRef> class_and_ivar = ivar_skipped_prefix.split('.'); +             +            if (class_and_ivar.first.size() && class_and_ivar.second.size()) +            { +                const ConstString class_name_cs(class_and_ivar.first); +                ClassDescriptorSP descriptor = ObjCLanguageRuntime::GetClassDescriptorFromClassName(class_name_cs); +                                 +                if (descriptor) +                { +                    const ConstString ivar_name_cs(class_and_ivar.second); +                    const char *ivar_name_cstr = ivar_name_cs.AsCString(); +                     +                    auto ivar_func = [&ret, ivar_name_cstr](const char *name, const char *type, lldb::addr_t offset_addr, uint64_t size) -> lldb::addr_t +                    { +                        if (!strcmp(name, ivar_name_cstr)) +                        { +                            ret = offset_addr; +                            return true; +                        } +                        return false; +                    }; + +                    descriptor->Describe(std::function<void (ObjCISA)>(nullptr), +                                         std::function<bool (const char *, const char *)>(nullptr), +                                         std::function<bool (const char *, const char *)>(nullptr), +                                         ivar_func); +                } +            } +        } +        else if (name_strref.startswith(class_prefix)) +        { +            llvm::StringRef class_skipped_prefix = name_strref.substr(class_prefix.size()); +            const ConstString class_name_cs(class_skipped_prefix); +            ClassDescriptorSP descriptor = GetClassDescriptorFromClassName(class_name_cs); +             +            if (descriptor) +                ret = descriptor->GetISA(); +        } +    } +     +    return ret; +} + +AppleObjCRuntimeV2::NonPointerISACache* +AppleObjCRuntimeV2::NonPointerISACache::CreateInstance (AppleObjCRuntimeV2& runtime, const lldb::ModuleSP& objc_module_sp) +{ +    Process* process(runtime.GetProcess()); +     +    Error error; +     +    auto objc_debug_isa_magic_mask = ExtractRuntimeGlobalSymbol(process, +                                                                ConstString("objc_debug_isa_magic_mask"), +                                                                objc_module_sp, +                                                                error); +    if (error.Fail()) +        return NULL; + +    auto objc_debug_isa_magic_value = ExtractRuntimeGlobalSymbol(process, +                                                                 ConstString("objc_debug_isa_magic_value"), +                                                                 objc_module_sp, +                                                                 error); +    if (error.Fail()) +        return NULL; + +    auto objc_debug_isa_class_mask = ExtractRuntimeGlobalSymbol(process, +                                                                ConstString("objc_debug_isa_class_mask"), +                                                                objc_module_sp, +                                                                error); +    if (error.Fail()) +        return NULL; + +    // we might want to have some rules to outlaw these other values (e.g if the mask is zero but the value is non-zero, ...) +     +    return new NonPointerISACache(runtime, +                                  objc_debug_isa_class_mask, +                                  objc_debug_isa_magic_mask, +                                  objc_debug_isa_magic_value); +} + +AppleObjCRuntimeV2::TaggedPointerVendorV2* +AppleObjCRuntimeV2::TaggedPointerVendorV2::CreateInstance (AppleObjCRuntimeV2& runtime, const lldb::ModuleSP& objc_module_sp) +{ +    Process* process(runtime.GetProcess()); +     +    Error error; +     +    auto objc_debug_taggedpointer_mask = ExtractRuntimeGlobalSymbol(process, +                                                                    ConstString("objc_debug_taggedpointer_mask"), +                                                                    objc_module_sp, +                                                                    error); +    if (error.Fail()) +        return new TaggedPointerVendorLegacy(runtime); +     +    auto objc_debug_taggedpointer_slot_shift = ExtractRuntimeGlobalSymbol(process, +                                                                          ConstString("objc_debug_taggedpointer_slot_shift"), +                                                                          objc_module_sp, +                                                                          error, +                                                                          true, +                                                                          4); +    if (error.Fail()) +        return new TaggedPointerVendorLegacy(runtime); +     +    auto objc_debug_taggedpointer_slot_mask = ExtractRuntimeGlobalSymbol(process, +                                                                          ConstString("objc_debug_taggedpointer_slot_mask"), +                                                                          objc_module_sp, +                                                                          error, +                                                                          true, +                                                                          4); +    if (error.Fail()) +        return new TaggedPointerVendorLegacy(runtime); + +    auto objc_debug_taggedpointer_payload_lshift = ExtractRuntimeGlobalSymbol(process, +                                                                              ConstString("objc_debug_taggedpointer_payload_lshift"), +                                                                              objc_module_sp, +                                                                              error, +                                                                              true, +                                                                              4); +    if (error.Fail()) +        return new TaggedPointerVendorLegacy(runtime); +     +    auto objc_debug_taggedpointer_payload_rshift = ExtractRuntimeGlobalSymbol(process, +                                                                              ConstString("objc_debug_taggedpointer_payload_rshift"), +                                                                              objc_module_sp, +                                                                              error, +                                                                              true, +                                                                              4); +    if (error.Fail()) +        return new TaggedPointerVendorLegacy(runtime); +     +    auto objc_debug_taggedpointer_classes = ExtractRuntimeGlobalSymbol(process, +                                                                       ConstString("objc_debug_taggedpointer_classes"), +                                                                       objc_module_sp, +                                                                       error, +                                                                       false); +    if (error.Fail()) +        return new TaggedPointerVendorLegacy(runtime); + +     +    // we might want to have some rules to outlaw these values (e.g if the table's address is zero) +     +    return new TaggedPointerVendorRuntimeAssisted(runtime, +                                                  objc_debug_taggedpointer_mask, +                                                  objc_debug_taggedpointer_slot_shift, +                                                  objc_debug_taggedpointer_slot_mask, +                                                  objc_debug_taggedpointer_payload_lshift, +                                                  objc_debug_taggedpointer_payload_rshift, +                                                  objc_debug_taggedpointer_classes); +} + +bool +AppleObjCRuntimeV2::TaggedPointerVendorLegacy::IsPossibleTaggedPointer (lldb::addr_t ptr) +{ +    return (ptr & 1); +} + +ObjCLanguageRuntime::ClassDescriptorSP +AppleObjCRuntimeV2::TaggedPointerVendorLegacy::GetClassDescriptor (lldb::addr_t ptr) +{ +    if (!IsPossibleTaggedPointer(ptr)) +        return ObjCLanguageRuntime::ClassDescriptorSP(); + +    uint32_t foundation_version = m_runtime.GetFoundationVersion(); +     +    if (foundation_version == LLDB_INVALID_MODULE_VERSION) +        return ObjCLanguageRuntime::ClassDescriptorSP(); +     +    uint64_t class_bits = (ptr & 0xE) >> 1; +    ConstString name; +     +    // TODO: make a table +    if (foundation_version >= 900) +    { +        switch (class_bits) +        { +            case 0: +                name = ConstString("NSAtom"); +                break; +            case 3: +                name = ConstString("NSNumber"); +                break; +            case 4: +                name = ConstString("NSDateTS"); +                break; +            case 5: +                name = ConstString("NSManagedObject"); +                break; +            case 6: +                name = ConstString("NSDate"); +                break; +            default: +                return ObjCLanguageRuntime::ClassDescriptorSP(); +        } +    } +    else +    { +        switch (class_bits) +        { +            case 1: +                name = ConstString("NSNumber"); +                break; +            case 5: +                name = ConstString("NSManagedObject"); +                break; +            case 6: +                name = ConstString("NSDate"); +                break; +            case 7: +                name = ConstString("NSDateTS"); +                break; +            default: +                return ObjCLanguageRuntime::ClassDescriptorSP(); +        } +    } +    return ClassDescriptorSP(new ClassDescriptorV2Tagged(name,ptr)); +} + +AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted::TaggedPointerVendorRuntimeAssisted (AppleObjCRuntimeV2& runtime, +                                                                                            uint64_t objc_debug_taggedpointer_mask, +                                                                                            uint32_t objc_debug_taggedpointer_slot_shift, +                                                                                            uint32_t objc_debug_taggedpointer_slot_mask, +                                                                                            uint32_t objc_debug_taggedpointer_payload_lshift, +                                                                                            uint32_t objc_debug_taggedpointer_payload_rshift, +                                                                                            lldb::addr_t objc_debug_taggedpointer_classes) : +TaggedPointerVendorV2(runtime), +m_cache(), +m_objc_debug_taggedpointer_mask(objc_debug_taggedpointer_mask), +m_objc_debug_taggedpointer_slot_shift(objc_debug_taggedpointer_slot_shift), +m_objc_debug_taggedpointer_slot_mask(objc_debug_taggedpointer_slot_mask), +m_objc_debug_taggedpointer_payload_lshift(objc_debug_taggedpointer_payload_lshift), +m_objc_debug_taggedpointer_payload_rshift(objc_debug_taggedpointer_payload_rshift), +m_objc_debug_taggedpointer_classes(objc_debug_taggedpointer_classes) +{ +} + +bool +AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted::IsPossibleTaggedPointer (lldb::addr_t ptr) +{ +    return (ptr & m_objc_debug_taggedpointer_mask) != 0; +} + +ObjCLanguageRuntime::ClassDescriptorSP +AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted::GetClassDescriptor (lldb::addr_t ptr) +{ +    ClassDescriptorSP actual_class_descriptor_sp; +    uint64_t data_payload; + +    if (!IsPossibleTaggedPointer(ptr)) +        return ObjCLanguageRuntime::ClassDescriptorSP(); +     +    uintptr_t slot = (ptr >> m_objc_debug_taggedpointer_slot_shift) & m_objc_debug_taggedpointer_slot_mask; +     +    CacheIterator iterator = m_cache.find(slot), +    end = m_cache.end(); +    if (iterator != end) +    { +        actual_class_descriptor_sp = iterator->second; +    } +    else +    { +        Process* process(m_runtime.GetProcess()); +        uintptr_t slot_ptr = slot*process->GetAddressByteSize()+m_objc_debug_taggedpointer_classes; +        Error error; +        uintptr_t slot_data = process->ReadPointerFromMemory(slot_ptr, error); +        if (error.Fail() || slot_data == 0 || slot_data == uintptr_t(LLDB_INVALID_ADDRESS)) +            return nullptr; +        actual_class_descriptor_sp = m_runtime.GetClassDescriptorFromISA((ObjCISA)slot_data); +        if (!actual_class_descriptor_sp) +            return ObjCLanguageRuntime::ClassDescriptorSP(); +        m_cache[slot] = actual_class_descriptor_sp; +    } +     +    data_payload = (((uint64_t)ptr << m_objc_debug_taggedpointer_payload_lshift) >> m_objc_debug_taggedpointer_payload_rshift); +     +    return ClassDescriptorSP(new ClassDescriptorV2Tagged(actual_class_descriptor_sp,data_payload)); +} + +AppleObjCRuntimeV2::NonPointerISACache::NonPointerISACache (AppleObjCRuntimeV2& runtime, +                                                            uint64_t objc_debug_isa_class_mask, +                                                            uint64_t objc_debug_isa_magic_mask, +                                                            uint64_t objc_debug_isa_magic_value) : +m_runtime(runtime), +m_cache(), +m_objc_debug_isa_class_mask(objc_debug_isa_class_mask), +m_objc_debug_isa_magic_mask(objc_debug_isa_magic_mask), +m_objc_debug_isa_magic_value(objc_debug_isa_magic_value) +{ +} + +ObjCLanguageRuntime::ClassDescriptorSP +AppleObjCRuntimeV2::NonPointerISACache::GetClassDescriptor (ObjCISA isa) +{ +    ObjCISA real_isa = 0; +    if (EvaluateNonPointerISA(isa, real_isa) == false) +        return ObjCLanguageRuntime::ClassDescriptorSP(); +    auto cache_iter = m_cache.find(real_isa); +    if (cache_iter != m_cache.end()) +        return cache_iter->second; +    auto descriptor_sp = m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA(real_isa); +    if (descriptor_sp) // cache only positive matches since the table might grow +        m_cache[real_isa] = descriptor_sp; +    return descriptor_sp; +} + +bool +AppleObjCRuntimeV2::NonPointerISACache::EvaluateNonPointerISA (ObjCISA isa, ObjCISA& ret_isa) +{ +    if ( (isa & ~m_objc_debug_isa_class_mask) == 0) +        return false; +    if ( (isa & m_objc_debug_isa_magic_mask) == m_objc_debug_isa_magic_value) +    { +        ret_isa = isa & m_objc_debug_isa_class_mask; +        return (ret_isa != 0); // this is a pointer so 0 is not a valid value +    } +    return false; +} + +ObjCLanguageRuntime::EncodingToTypeSP +AppleObjCRuntimeV2::GetEncodingToType () +{ +    if (!m_encoding_to_type_sp) +        m_encoding_to_type_sp.reset(new AppleObjCTypeEncodingParser(*this)); +    return m_encoding_to_type_sp; +} + +lldb_private::AppleObjCRuntime::ObjCISA +AppleObjCRuntimeV2::GetPointerISA (ObjCISA isa) +{ +    ObjCISA ret = isa; +     +    if (m_non_pointer_isa_cache_ap) +        m_non_pointer_isa_cache_ap->EvaluateNonPointerISA(isa, ret); +     +    return ret; +} diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h new file mode 100644 index 0000000000000..96b47e8770f99 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h @@ -0,0 +1,336 @@ +//===-- AppleObjCRuntimeV2.h ------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AppleObjCRuntimeV2_h_ +#define liblldb_AppleObjCRuntimeV2_h_ + +// C Includes +// C++ Includes +#include <map> +#include <memory> + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "AppleObjCRuntime.h" + +class RemoteNXMapTable; + +namespace lldb_private { + +class AppleObjCRuntimeV2 : +        public AppleObjCRuntime +{ +public: +    ~AppleObjCRuntimeV2() override = default; + +    //------------------------------------------------------------------ +    // Static Functions +    //------------------------------------------------------------------ +    static void +    Initialize(); +     +    static void +    Terminate(); +     +    static lldb_private::LanguageRuntime * +    CreateInstance (Process *process, lldb::LanguageType language); +     +    static lldb_private::ConstString +    GetPluginNameStatic(); + +    static bool classof(const ObjCLanguageRuntime* runtime) +    { +        switch (runtime->GetRuntimeVersion()) +        { +            case ObjCRuntimeVersions::eAppleObjC_V2: +                return true; +            default: +                return false; +        } +    } + +    // These are generic runtime functions: +    bool +    GetDynamicTypeAndAddress(ValueObject &in_value, +                             lldb::DynamicValueType use_dynamic, +                             TypeAndOrName &class_type_or_name, +                             Address &address, +                             Value::ValueType &value_type) override; +     +    UtilityFunction * +    CreateObjectChecker(const char *) override; + +    //------------------------------------------------------------------ +    // PluginInterface protocol +    //------------------------------------------------------------------ +    ConstString +    GetPluginName() override; +     +    uint32_t +    GetPluginVersion() override; +     +    ObjCRuntimeVersions +    GetRuntimeVersion() const override +    { +        return ObjCRuntimeVersions::eAppleObjC_V2; +    } + +    size_t +    GetByteOffsetForIvar(CompilerType &parent_qual_type, const char *ivar_name) override; + +    void +    UpdateISAToDescriptorMapIfNeeded() override; +     +    ConstString +    GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa) override; +     +    ClassDescriptorSP +    GetClassDescriptor(ValueObject& in_value) override; +     +    ClassDescriptorSP +    GetClassDescriptorFromISA(ObjCISA isa) override; +     +    DeclVendor * +    GetDeclVendor() override; +     +    lldb::addr_t +    LookupRuntimeSymbol(const ConstString &name) override; +     +    EncodingToTypeSP +    GetEncodingToType() override; +     +    TaggedPointerVendor* +    GetTaggedPointerVendor() override +    { +        return m_tagged_pointer_vendor_ap.get(); +    } +     +    // none of these are valid ISAs - we use them to infer the type +    // of tagged pointers - if we have something meaningful to say +    // we report an actual type - otherwise, we just say tagged +    // there is no connection between the values here and the tagged pointers map +    static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA = 1; +    static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSAtom = 2; +    static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSNumber = 3; +    static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDateTS = 4; +    static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSManagedObject = 5; +    static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDate = 6; + +protected: +    lldb::BreakpointResolverSP +    CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp, bool throw_bp) override; + +private: +    class HashTableSignature +    { +    public: +        HashTableSignature (); + +        bool +        NeedsUpdate (Process *process, +                     AppleObjCRuntimeV2 *runtime, +                     RemoteNXMapTable &hash_table); +         +        void +        UpdateSignature (const RemoteNXMapTable &hash_table); + +    protected: +        uint32_t m_count; +        uint32_t m_num_buckets; +        lldb::addr_t m_buckets_ptr; +    }; + +    class NonPointerISACache +    { +    public: +        static NonPointerISACache* +        CreateInstance (AppleObjCRuntimeV2& runtime, +                        const lldb::ModuleSP& objc_module_sp); +         + +        ObjCLanguageRuntime::ClassDescriptorSP +        GetClassDescriptor (ObjCISA isa); + +    private: +        NonPointerISACache (AppleObjCRuntimeV2& runtime, +                            uint64_t objc_debug_isa_class_mask, +                            uint64_t objc_debug_isa_magic_mask, +                            uint64_t objc_debug_isa_magic_value); +         +        bool +        EvaluateNonPointerISA (ObjCISA isa, ObjCISA& ret_isa); +         +        AppleObjCRuntimeV2&                                         m_runtime; +        std::map<ObjCISA,ObjCLanguageRuntime::ClassDescriptorSP>    m_cache; +        uint64_t                                                    m_objc_debug_isa_class_mask; +        uint64_t                                                    m_objc_debug_isa_magic_mask; +        uint64_t                                                    m_objc_debug_isa_magic_value; + +        friend class AppleObjCRuntimeV2; +         +        DISALLOW_COPY_AND_ASSIGN(NonPointerISACache); +    }; +     +    class TaggedPointerVendorV2 : public ObjCLanguageRuntime::TaggedPointerVendor +    { +    public: +        ~TaggedPointerVendorV2() override = default; + +        static TaggedPointerVendorV2* +        CreateInstance (AppleObjCRuntimeV2& runtime, +                        const lldb::ModuleSP& objc_module_sp); + +    protected: +        AppleObjCRuntimeV2&                                         m_runtime; +         +        TaggedPointerVendorV2 (AppleObjCRuntimeV2& runtime) : +        TaggedPointerVendor(), +        m_runtime(runtime) +        { +        } + +    private: +        DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorV2); +    }; +     +    class TaggedPointerVendorRuntimeAssisted : public TaggedPointerVendorV2 +    { +    public: +        bool +        IsPossibleTaggedPointer(lldb::addr_t ptr) override; +         +        ObjCLanguageRuntime::ClassDescriptorSP +        GetClassDescriptor(lldb::addr_t ptr) override; + +    protected: +        TaggedPointerVendorRuntimeAssisted (AppleObjCRuntimeV2& runtime, +                                             uint64_t objc_debug_taggedpointer_mask, +                                             uint32_t objc_debug_taggedpointer_slot_shift, +                                             uint32_t objc_debug_taggedpointer_slot_mask, +                                             uint32_t objc_debug_taggedpointer_payload_lshift, +                                             uint32_t objc_debug_taggedpointer_payload_rshift, +                                             lldb::addr_t objc_debug_taggedpointer_classes); +         +        typedef std::map<uint8_t,ObjCLanguageRuntime::ClassDescriptorSP> Cache; +        typedef Cache::iterator CacheIterator; +        Cache                                                       m_cache; +        uint64_t                                                    m_objc_debug_taggedpointer_mask; +        uint32_t                                                    m_objc_debug_taggedpointer_slot_shift; +        uint32_t                                                    m_objc_debug_taggedpointer_slot_mask; +        uint32_t                                                    m_objc_debug_taggedpointer_payload_lshift; +        uint32_t                                                    m_objc_debug_taggedpointer_payload_rshift; +        lldb::addr_t                                                m_objc_debug_taggedpointer_classes; +         +        friend class AppleObjCRuntimeV2::TaggedPointerVendorV2; +         +        DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorRuntimeAssisted); +    }; +     +    class TaggedPointerVendorLegacy : public TaggedPointerVendorV2 +    { +    public: +        bool +        IsPossibleTaggedPointer(lldb::addr_t ptr) override; +         +        ObjCLanguageRuntime::ClassDescriptorSP +        GetClassDescriptor (lldb::addr_t ptr) override; + +    protected: +        TaggedPointerVendorLegacy (AppleObjCRuntimeV2& runtime) : +        TaggedPointerVendorV2 (runtime) +        { +        } +         +        friend class AppleObjCRuntimeV2::TaggedPointerVendorV2; +         +        DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorLegacy); +    }; +     +    struct DescriptorMapUpdateResult +    { +        bool update_ran; +        bool any_found; +         +        DescriptorMapUpdateResult (bool ran, +                                   bool found) +        { +            update_ran = ran; +            any_found = found; +        } +         +        static DescriptorMapUpdateResult +        Fail () +        { +            return {false, false}; +        } +         +        static DescriptorMapUpdateResult +        Success () +        { +            return {true, true}; +        } +    }; +     +    AppleObjCRuntimeV2 (Process *process, +                        const lldb::ModuleSP &objc_module_sp); +     +    ObjCISA +    GetPointerISA (ObjCISA isa); +     +    bool +    IsTaggedPointer(lldb::addr_t ptr); +     +    lldb::addr_t +    GetISAHashTablePointer (); + +    bool +    UpdateISAToDescriptorMapFromMemory (RemoteNXMapTable &hash_table); +     +    bool +    UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table); +     +    uint32_t +    ParseClassInfoArray (const lldb_private::DataExtractor &data, +                         uint32_t num_class_infos); +     +    DescriptorMapUpdateResult +    UpdateISAToDescriptorMapSharedCache (); +     +    void +    WarnIfNoClassesCached (); + +    lldb::addr_t +    GetSharedCacheReadOnlyAddress(); +     +    friend class ClassDescriptorV2; + +    std::unique_ptr<UtilityFunction>        m_get_class_info_code; +    lldb::addr_t                            m_get_class_info_args; +    Mutex                                   m_get_class_info_args_mutex; + +    std::unique_ptr<UtilityFunction>        m_get_shared_cache_class_info_code; +    lldb::addr_t                            m_get_shared_cache_class_info_args; +    Mutex                                   m_get_shared_cache_class_info_args_mutex; + +    std::unique_ptr<DeclVendor>             m_decl_vendor_ap; +    lldb::addr_t                            m_isa_hash_table_ptr; +    HashTableSignature                      m_hash_signature; +    bool                                    m_has_object_getClass; +    bool                                    m_loaded_objc_opt; +    std::unique_ptr<NonPointerISACache>     m_non_pointer_isa_cache_ap; +    std::unique_ptr<TaggedPointerVendor>    m_tagged_pointer_vendor_ap; +    EncodingToTypeSP                        m_encoding_to_type_sp; +    bool                                    m_noclasses_warning_emitted; +}; +     +} // namespace lldb_private + +#endif // liblldb_AppleObjCRuntimeV2_h_ diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp new file mode 100644 index 0000000000000..d38a076ad5d74 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp @@ -0,0 +1,1144 @@ +//===-- AppleObjCTrampolineHandler.cpp ----------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleObjCTrampolineHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "AppleThreadPlanStepThroughObjCTrampoline.h" + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +const char *AppleObjCTrampolineHandler::g_lookup_implementation_function_name = "__lldb_objc_find_implementation_for_selector"; +const char *AppleObjCTrampolineHandler::g_lookup_implementation_function_code = NULL; +const char *AppleObjCTrampolineHandler::g_lookup_implementation_with_stret_function_code = "                               \n\ +extern \"C\"                                                                                                    \n\ +{                                                                                                               \n\ +    extern void *class_getMethodImplementation(void *objc_class, void *sel);                                    \n\ +    extern void *class_getMethodImplementation_stret(void *objc_class, void *sel);                              \n\ +    extern void * object_getClass (id object);                                                                  \n\ +    extern void * sel_getUid(char *name);                                                                       \n\ +    extern int printf(const char *format, ...);                                                                 \n\ +}                                                                                                               \n\ +extern \"C\" void * __lldb_objc_find_implementation_for_selector (void *object,                                 \n\ +                                                    void *sel,                                                  \n\ +                                                    int is_stret,                                               \n\ +                                                    int is_super,                                               \n\ +                                                    int is_super2,                                              \n\ +                                                    int is_fixup,                                               \n\ +                                                    int is_fixed,                                               \n\ +                                                    int debug)                                                  \n\ +{                                                                                                               \n\ +    struct __lldb_imp_return_struct                                                                             \n\ +    {                                                                                                           \n\ +        void *class_addr;                                                                                       \n\ +        void *sel_addr;                                                                                         \n\ +        void *impl_addr;                                                                                        \n\ +    };                                                                                                          \n\ +                                                                                                                \n\ +    struct __lldb_objc_class {                                                                                  \n\ +        void *isa;                                                                                              \n\ +        void *super_ptr;                                                                                        \n\ +    };                                                                                                          \n\ +    struct __lldb_objc_super {                                                                                  \n\ +        void *reciever;                                                                                         \n\ +        struct __lldb_objc_class *class_ptr;                                                                    \n\ +    };                                                                                                          \n\ +    struct __lldb_msg_ref {                                                                                     \n\ +        void *dont_know;                                                                                        \n\ +        void *sel;                                                                                              \n\ +    };                                                                                                          \n\ +                                                                                                                \n\ +    struct __lldb_imp_return_struct return_struct;                                                              \n\ +                                                                                                                \n\ +    if (debug)                                                                                                  \n\ +        printf (\"\\n*** Called with obj: 0x%p sel: 0x%p is_stret: %d is_super: %d, \"                          \n\ +                \"is_super2: %d, is_fixup: %d, is_fixed: %d\\n\",                                               \n\ +                 object, sel, is_stret, is_super, is_super2, is_fixup, is_fixed);                               \n\ +    if (is_super)                                                                                               \n\ +    {                                                                                                           \n\ +        if (is_super2)                                                                                          \n\ +        {                                                                                                       \n\ +            return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr->super_ptr;                    \n\ +        }                                                                                                       \n\ +        else                                                                                                    \n\ +        {                                                                                                       \n\ +            return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr;                               \n\ +        }                                                                                                       \n\ +    }                                                                                                           \n\ +    else                                                                                                        \n\ +    {                                                                                                           \n\ +        // This code seems a little funny, but has its reasons...                                               \n\ +        // The call to [object class] is here because if this is a class, and has not been called into          \n\ +        // yet, we need to do something to force the class to initialize itself.                                \n\ +        // Then the call to object_getClass will actually return the correct class, either the class            \n\ +        // if object is a class instance, or the meta-class if it is a class pointer.                           \n\ +        void *class_ptr = (void *) [(id) object class];                                                         \n\ +        return_struct.class_addr = (id)  object_getClass((id) object);                                          \n\ +        if (debug)                                                                                              \n\ +        {                                                                                                       \n\ +            if (class_ptr == object)                                                                            \n\ +            {                                                                                                   \n\ +                printf (\"Found a class object, need to use the meta class %p -> %p\\n\",                       \n\ +                        class_ptr, return_struct.class_addr);                                                   \n\ +            }                                                                                                   \n\ +            else                                                                                                \n\ +            {                                                                                                   \n\ +                 printf (\"[object class] returned: %p object_getClass: %p.\\n\",                               \n\ +                 class_ptr, return_struct.class_addr);                                                          \n\ +            }                                                                                                   \n\ +        }                                                                                                       \n\ +    }                                                                                                           \n\ +                                                                                                                \n\ +    if (is_fixup)                                                                                               \n\ +    {                                                                                                           \n\ +        if (is_fixed)                                                                                           \n\ +        {                                                                                                       \n\ +            return_struct.sel_addr = ((__lldb_msg_ref *) sel)->sel;                                             \n\ +        }                                                                                                       \n\ +        else                                                                                                    \n\ +        {                                                                                                       \n\ +            char *sel_name = (char *) ((__lldb_msg_ref *) sel)->sel;                                            \n\ +            return_struct.sel_addr = sel_getUid (sel_name);                                                     \n\ +            if (debug)                                                                                          \n\ +                printf (\"\\n*** Got fixed up selector: %p for name %s.\\n\",                                   \n\ +                        return_struct.sel_addr, sel_name);                                                      \n\ +        }                                                                                                       \n\ +    }                                                                                                           \n\ +    else                                                                                                        \n\ +    {                                                                                                           \n\ +        return_struct.sel_addr = sel;                                                                           \n\ +    }                                                                                                           \n\ +                                                                                                                \n\ +    if (is_stret)                                                                                               \n\ +    {                                                                                                           \n\ +        return_struct.impl_addr = class_getMethodImplementation_stret (return_struct.class_addr,                \n\ +                                                                       return_struct.sel_addr);                 \n\ +    }                                                                                                           \n\ +    else                                                                                                        \n\ +    {                                                                                                           \n\ +        return_struct.impl_addr = class_getMethodImplementation (return_struct.class_addr,                      \n\ +                                                                       return_struct.sel_addr);                 \n\ +    }                                                                                                           \n\ +    if (debug)                                                                                                  \n\ +        printf (\"\\n*** Returning implementation: %p.\\n\", return_struct.impl_addr);                          \n\ +                                                                                                                \n\ +    return return_struct.impl_addr;                                                                             \n\ +}                                                                                                               \n\ +"; +const char *AppleObjCTrampolineHandler::g_lookup_implementation_no_stret_function_code = "                      \n\ +extern \"C\"                                                                                                    \n\ +{                                                                                                               \n\ +    extern void *class_getMethodImplementation(void *objc_class, void *sel);                                    \n\ +    extern void * object_getClass (id object);                                                                  \n\ +    extern void * sel_getUid(char *name);                                                                       \n\ +    extern int printf(const char *format, ...);                                                                 \n\ +}                                                                                                               \n\ +extern \"C\" void * __lldb_objc_find_implementation_for_selector (void *object,                                 \n\ +                                                    void *sel,                                                  \n\ +                                                    int is_stret,                                               \n\ +                                                    int is_super,                                               \n\ +                                                    int is_super2,                                              \n\ +                                                    int is_fixup,                                               \n\ +                                                    int is_fixed,                                               \n\ +                                                    int debug)                                                  \n\ +{                                                                                                               \n\ +    struct __lldb_imp_return_struct                                                                             \n\ +    {                                                                                                           \n\ +        void *class_addr;                                                                                       \n\ +        void *sel_addr;                                                                                         \n\ +        void *impl_addr;                                                                                        \n\ +    };                                                                                                          \n\ +                                                                                                                \n\ +    struct __lldb_objc_class {                                                                                  \n\ +        void *isa;                                                                                              \n\ +        void *super_ptr;                                                                                        \n\ +    };                                                                                                          \n\ +    struct __lldb_objc_super {                                                                                  \n\ +        void *reciever;                                                                                         \n\ +        struct __lldb_objc_class *class_ptr;                                                                    \n\ +    };                                                                                                          \n\ +    struct __lldb_msg_ref {                                                                                     \n\ +        void *dont_know;                                                                                        \n\ +        void *sel;                                                                                              \n\ +    };                                                                                                          \n\ +                                                                                                                \n\ +    struct __lldb_imp_return_struct return_struct;                                                              \n\ +                                                                                                                \n\ +    if (debug)                                                                                                  \n\ +        printf (\"\\n*** Called with obj: 0x%p sel: 0x%p is_stret: %d is_super: %d, \"                          \n\ +                \"is_super2: %d, is_fixup: %d, is_fixed: %d\\n\",                                               \n\ +                 object, sel, is_stret, is_super, is_super2, is_fixup, is_fixed);                               \n\ +    if (is_super)                                                                                               \n\ +    {                                                                                                           \n\ +        if (is_super2)                                                                                          \n\ +        {                                                                                                       \n\ +            return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr->super_ptr;                    \n\ +        }                                                                                                       \n\ +        else                                                                                                    \n\ +        {                                                                                                       \n\ +            return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr;                               \n\ +        }                                                                                                       \n\ +    }                                                                                                           \n\ +    else                                                                                                        \n\ +    {                                                                                                           \n\ +        // This code seems a little funny, but has its reasons...                                               \n\ +        // The call to [object class] is here because if this is a class, and has not been called into          \n\ +        // yet, we need to do something to force the class to initialize itself.                                \n\ +        // Then the call to object_getClass will actually return the correct class, either the class            \n\ +        // if object is a class instance, or the meta-class if it is a class pointer.                           \n\ +        void *class_ptr = (void *) [(id) object class];                                                         \n\ +        return_struct.class_addr = (id)  object_getClass((id) object);                                          \n\ +        if (debug)                                                                                              \n\ +        {                                                                                                       \n\ +            if (class_ptr == object)                                                                            \n\ +            {                                                                                                   \n\ +                printf (\"Found a class object, need to return the meta class %p -> %p\\n\",                    \n\ +                        class_ptr, return_struct.class_addr);                                                   \n\ +            }                                                                                                   \n\ +            else                                                                                                \n\ +            {                                                                                                   \n\ +                 printf (\"[object class] returned: %p object_getClass: %p.\\n\",                               \n\ +                 class_ptr, return_struct.class_addr);                                                          \n\ +            }                                                                                                   \n\ +        }                                                                                                       \n\ +    }                                                                                                           \n\ +                                                                                                                \n\ +    if (is_fixup)                                                                                               \n\ +    {                                                                                                           \n\ +        if (is_fixed)                                                                                           \n\ +        {                                                                                                       \n\ +            return_struct.sel_addr = ((__lldb_msg_ref *) sel)->sel;                                             \n\ +        }                                                                                                       \n\ +        else                                                                                                    \n\ +        {                                                                                                       \n\ +            char *sel_name = (char *) ((__lldb_msg_ref *) sel)->sel;                                            \n\ +            return_struct.sel_addr = sel_getUid (sel_name);                                                     \n\ +            if (debug)                                                                                          \n\ +                printf (\"\\n*** Got fixed up selector: %p for name %s.\\n\",                                   \n\ +                        return_struct.sel_addr, sel_name);                                                      \n\ +        }                                                                                                       \n\ +    }                                                                                                           \n\ +    else                                                                                                        \n\ +    {                                                                                                           \n\ +        return_struct.sel_addr = sel;                                                                           \n\ +    }                                                                                                           \n\ +                                                                                                                \n\ +    return_struct.impl_addr = class_getMethodImplementation (return_struct.class_addr,                          \n\ +                                                             return_struct.sel_addr);                           \n\ +    if (debug)                                                                                                  \n\ +        printf (\"\\n*** Returning implementation: 0x%p.\\n\", return_struct.impl_addr);                        \n\ +                                                                                                                \n\ +    return return_struct.impl_addr;                                                                             \n\ +}                                                                                                               \n\ +"; + +AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr) : +    m_valid (true), +    m_owner(owner), +    m_header_addr (header_addr), +    m_code_start_addr(0), +    m_code_end_addr (0), +    m_next_region (0) +{ +    SetUpRegion (); +} + +AppleObjCTrampolineHandler::~AppleObjCTrampolineHandler() +{ +} + +void +AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::SetUpRegion() +{ +    // The header looks like: +    // +    //   uint16_t headerSize +    //   uint16_t descSize +    //   uint32_t descCount +    //   void * next +    // +    // First read in the header: +     +    char memory_buffer[16]; +    ProcessSP process_sp = m_owner->GetProcessSP(); +    if (!process_sp) +        return; +    DataExtractor data(memory_buffer, sizeof(memory_buffer),  +                       process_sp->GetByteOrder(), +                       process_sp->GetAddressByteSize()); +    size_t actual_size = 8 + process_sp->GetAddressByteSize(); +    Error error; +    size_t bytes_read = process_sp->ReadMemory (m_header_addr, memory_buffer, actual_size, error); +    if (bytes_read != actual_size) +    { +        m_valid = false; +        return; +    } +     +    lldb::offset_t offset = 0; +    const uint16_t header_size = data.GetU16(&offset); +    const uint16_t descriptor_size = data.GetU16(&offset); +    const size_t num_descriptors = data.GetU32(&offset); +     +    m_next_region = data.GetPointer(&offset); +     +    // If the header size is 0, that means we've come in too early before this data is set up. +    // Set ourselves as not valid, and continue. +    if (header_size == 0 || num_descriptors == 0) +    { +        m_valid = false; +        return; +    } +     +    // Now read in all the descriptors: +    // The descriptor looks like: +    // +    // uint32_t offset +    // uint32_t flags +    // +    // Where offset is either 0 - in which case it is unused, or +    // it is the offset of the vtable code from the beginning of the descriptor record. +    // Below, we'll convert that into an absolute code address, since I don't want to have +    // to compute it over and over. +     +    // Ingest the whole descriptor array: +    const lldb::addr_t desc_ptr = m_header_addr + header_size; +    const size_t desc_array_size = num_descriptors * descriptor_size; +    DataBufferSP data_sp(new DataBufferHeap (desc_array_size, '\0')); +    uint8_t* dst = (uint8_t*)data_sp->GetBytes(); + +    DataExtractor desc_extractor (dst, desc_array_size, +                                  process_sp->GetByteOrder(), +                                  process_sp->GetAddressByteSize()); +    bytes_read = process_sp->ReadMemory(desc_ptr, dst, desc_array_size, error); +    if (bytes_read != desc_array_size) +    { +        m_valid = false; +        return; +    } +     +    // The actual code for the vtables will be laid out consecutively, so I also +    // compute the start and end of the whole code block. + +    offset = 0; +    m_code_start_addr = 0; +    m_code_end_addr = 0; + +    for (size_t i = 0; i < num_descriptors; i++) +    { +        lldb::addr_t start_offset = offset; +        uint32_t voffset = desc_extractor.GetU32 (&offset); +        uint32_t flags  = desc_extractor.GetU32 (&offset); +        lldb::addr_t code_addr = desc_ptr + start_offset + voffset; +        m_descriptors.push_back (VTableDescriptor(flags, code_addr)); +         +        if (m_code_start_addr == 0 || code_addr < m_code_start_addr) +            m_code_start_addr = code_addr; +        if (code_addr > m_code_end_addr) +            m_code_end_addr = code_addr; +             +        offset = start_offset + descriptor_size; +    } +    // Finally, a little bird told me that all the vtable code blocks are the same size.   +    // Let's compute the blocks and if they are all the same add the size to the code end address: +    lldb::addr_t code_size = 0; +    bool all_the_same = true; +    for (size_t i = 0; i < num_descriptors - 1; i++) +    { +        lldb::addr_t this_size = m_descriptors[i + 1].code_start - m_descriptors[i].code_start; +        if (code_size == 0) +            code_size = this_size; +        else +        { +            if (this_size != code_size) +                all_the_same = false; +            if (this_size > code_size) +                code_size = this_size; +        } +    } +    if (all_the_same) +        m_code_end_addr += code_size; +} + +bool  +AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::AddressInRegion (lldb::addr_t addr, uint32_t &flags) +{ +    if (!IsValid()) +        return false; +         +    if (addr < m_code_start_addr || addr > m_code_end_addr) +        return false; +         +    std::vector<VTableDescriptor>::iterator pos, end = m_descriptors.end(); +    for (pos = m_descriptors.begin(); pos != end; pos++) +    { +        if (addr <= (*pos).code_start) +        { +            flags = (*pos).flags; +            return true; +        } +    } +    return false; +} + +void +AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::Dump (Stream &s) +{ +    s.Printf ("Header addr: 0x%" PRIx64 " Code start: 0x%" PRIx64 " Code End: 0x%" PRIx64 " Next: 0x%" PRIx64 "\n", +              m_header_addr, m_code_start_addr, m_code_end_addr, m_next_region); +    size_t num_elements = m_descriptors.size(); +    for (size_t i = 0; i < num_elements; i++) +    { +        s.Indent(); +        s.Printf ("Code start: 0x%" PRIx64 " Flags: %d\n", m_descriptors[i].code_start, m_descriptors[i].flags); +    } +} +         +AppleObjCTrampolineHandler::AppleObjCVTables::AppleObjCVTables (const ProcessSP &process_sp,  +                                                                const ModuleSP &objc_module_sp) : +    m_process_wp (), +    m_trampoline_header (LLDB_INVALID_ADDRESS), +    m_trampolines_changed_bp_id (LLDB_INVALID_BREAK_ID), +    m_objc_module_sp (objc_module_sp) +{ +    if (process_sp) +        m_process_wp = process_sp; +} + +AppleObjCTrampolineHandler::AppleObjCVTables::~AppleObjCVTables() +{ +    ProcessSP process_sp = GetProcessSP (); +    if (process_sp) +    { +        if (m_trampolines_changed_bp_id != LLDB_INVALID_BREAK_ID) +            process_sp->GetTarget().RemoveBreakpointByID (m_trampolines_changed_bp_id); +    } +} + +bool +AppleObjCTrampolineHandler::AppleObjCVTables::InitializeVTableSymbols () +{ +    if (m_trampoline_header != LLDB_INVALID_ADDRESS) +        return true; + +    ProcessSP process_sp = GetProcessSP (); +    if (process_sp) +    { +        Target &target = process_sp->GetTarget(); +         +        const ModuleList &target_modules = target.GetImages(); +        Mutex::Locker modules_locker(target_modules.GetMutex()); +        size_t num_modules = target_modules.GetSize(); +        if (!m_objc_module_sp) +        { +            for (size_t i = 0; i < num_modules; i++) +            { +                if (process_sp->GetObjCLanguageRuntime()->IsModuleObjCLibrary (target_modules.GetModuleAtIndexUnlocked(i))) +                { +                    m_objc_module_sp = target_modules.GetModuleAtIndexUnlocked(i); +                    break; +                } +            } +        } +         +        if (m_objc_module_sp) +        { +            ConstString trampoline_name ("gdb_objc_trampolines"); +            const Symbol *trampoline_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (trampoline_name,  +                                                                                                eSymbolTypeData); +            if (trampoline_symbol != NULL) +            { +                m_trampoline_header = trampoline_symbol->GetLoadAddress(&target); +                if (m_trampoline_header == LLDB_INVALID_ADDRESS) +                    return false; +                 +                // Next look up the "changed" symbol and set a breakpoint on that... +                ConstString changed_name ("gdb_objc_trampolines_changed"); +                const Symbol *changed_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (changed_name,  +                                                                                                 eSymbolTypeCode); +                if (changed_symbol != NULL) +                { +                    const Address changed_symbol_addr = changed_symbol->GetAddress(); +                    if (!changed_symbol_addr.IsValid()) +                        return false; +                         +                    lldb::addr_t changed_addr = changed_symbol_addr.GetOpcodeLoadAddress (&target); +                    if (changed_addr != LLDB_INVALID_ADDRESS) +                    { +                        BreakpointSP trampolines_changed_bp_sp = target.CreateBreakpoint (changed_addr, true, false); +                        if (trampolines_changed_bp_sp) +                        { +                            m_trampolines_changed_bp_id = trampolines_changed_bp_sp->GetID(); +                            trampolines_changed_bp_sp->SetCallback (RefreshTrampolines, this, true); +                            trampolines_changed_bp_sp->SetBreakpointKind ("objc-trampolines-changed"); +                            return true; +                        } +                    } +                } +            } +        } +    } +    return false; +} +     +bool  +AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines (void *baton,  +                                                                  StoppointCallbackContext *context,  +                                                                  lldb::user_id_t break_id,  +                                                                  lldb::user_id_t break_loc_id) +{ +    AppleObjCVTables *vtable_handler = (AppleObjCVTables *) baton; +    if (vtable_handler->InitializeVTableSymbols()) +    { +        // The Update function is called with the address of an added region.  So we grab that address, and +        // feed it into ReadRegions.  Of course, our friend the ABI will get the values for us. +        ExecutionContext exe_ctx (context->exe_ctx_ref); +        Process *process = exe_ctx.GetProcessPtr(); +        const ABI *abi = process->GetABI().get(); +         +        ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext(); +        ValueList argument_values; +        Value input_value; +        CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + +        input_value.SetValueType (Value::eValueTypeScalar); +        //input_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type); +        input_value.SetCompilerType (clang_void_ptr_type); +        argument_values.PushValue(input_value); +         +        bool success = abi->GetArgumentValues (exe_ctx.GetThreadRef(), argument_values); +        if (!success) +            return false; +             +        // Now get a pointer value from the zeroth argument. +        Error error; +        DataExtractor data; +        error = argument_values.GetValueAtIndex(0)->GetValueAsData (&exe_ctx,  +                                                                    data,  +                                                                    0, +                                                                    NULL); +        lldb::offset_t offset = 0; +        lldb::addr_t region_addr = data.GetPointer(&offset); +         +        if (region_addr != 0) +            vtable_handler->ReadRegions(region_addr); +    } +    return false; +} + +bool +AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions () +{ +    // The no argument version reads the start region from the value of the gdb_regions_header, and  +    // gets started from there. +     +    m_regions.clear(); +    if (!InitializeVTableSymbols()) +        return false; +    Error error; +    ProcessSP process_sp = GetProcessSP (); +    if (process_sp) +    { +        lldb::addr_t region_addr = process_sp->ReadPointerFromMemory (m_trampoline_header, error); +        if (error.Success()) +            return ReadRegions (region_addr); +    } +    return false; +} + +bool +AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions (lldb::addr_t region_addr) +{ +    ProcessSP process_sp = GetProcessSP (); +    if (!process_sp) +        return false; +         +    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); +     +    // We aren't starting at the trampoline symbol. +    InitializeVTableSymbols (); +    lldb::addr_t next_region = region_addr; +     +    // Read in the sizes of the headers. +    while (next_region != 0)  +    { +        m_regions.push_back (VTableRegion(this, next_region)); +        if (!m_regions.back().IsValid()) +        { +            m_regions.clear(); +            return false; +        } +        if (log) +        { +            StreamString s; +            m_regions.back().Dump(s); +            log->Printf("Read vtable region: \n%s", s.GetData()); +        } +         +        next_region = m_regions.back().GetNextRegionAddr(); +    } +     +    return true; +} +     +bool +AppleObjCTrampolineHandler::AppleObjCVTables::IsAddressInVTables (lldb::addr_t addr, uint32_t &flags) +{ +    region_collection::iterator pos, end = m_regions.end(); +    for (pos = m_regions.begin(); pos != end; pos++) +    { +        if ((*pos).AddressInRegion (addr, flags)) +            return true; +    } +    return false; +} + +const AppleObjCTrampolineHandler::DispatchFunction +AppleObjCTrampolineHandler::g_dispatch_functions[] = +{ +    // NAME                              STRET  SUPER  SUPER2  FIXUP TYPE +    {"objc_msgSend",                     false, false, false, DispatchFunction::eFixUpNone    }, +    {"objc_msgSend_fixup",               false, false, false, DispatchFunction::eFixUpToFix   }, +    {"objc_msgSend_fixedup",             false, false, false, DispatchFunction::eFixUpFixed   }, +    {"objc_msgSend_stret",               true,  false, false, DispatchFunction::eFixUpNone    }, +    {"objc_msgSend_stret_fixup",         true,  false, false, DispatchFunction::eFixUpToFix   }, +    {"objc_msgSend_stret_fixedup",       true,  false, false, DispatchFunction::eFixUpFixed   }, +    {"objc_msgSend_fpret",               false, false, false, DispatchFunction::eFixUpNone    }, +    {"objc_msgSend_fpret_fixup",         false, false, false, DispatchFunction::eFixUpToFix   }, +    {"objc_msgSend_fpret_fixedup",       false, false, false, DispatchFunction::eFixUpFixed   }, +    {"objc_msgSend_fp2ret",              false, false,  true, DispatchFunction::eFixUpNone    }, +    {"objc_msgSend_fp2ret_fixup",        false, false,  true, DispatchFunction::eFixUpToFix   }, +    {"objc_msgSend_fp2ret_fixedup",      false, false,  true, DispatchFunction::eFixUpFixed   }, +    {"objc_msgSendSuper",                false, true,  false, DispatchFunction::eFixUpNone    }, +    {"objc_msgSendSuper_stret",          true,  true,  false, DispatchFunction::eFixUpNone    }, +    {"objc_msgSendSuper2",               false, true,   true, DispatchFunction::eFixUpNone    }, +    {"objc_msgSendSuper2_fixup",         false, true,   true, DispatchFunction::eFixUpToFix   }, +    {"objc_msgSendSuper2_fixedup",       false, true,   true, DispatchFunction::eFixUpFixed   }, +    {"objc_msgSendSuper2_stret",         true,  true,   true, DispatchFunction::eFixUpNone    }, +    {"objc_msgSendSuper2_stret_fixup",   true,  true,   true, DispatchFunction::eFixUpToFix   }, +    {"objc_msgSendSuper2_stret_fixedup", true,  true,   true, DispatchFunction::eFixUpFixed   }, +}; + +AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (const ProcessSP &process_sp,  +                                                        const ModuleSP &objc_module_sp) : +    m_process_wp (), +    m_objc_module_sp (objc_module_sp), +    m_impl_fn_addr (LLDB_INVALID_ADDRESS), +    m_impl_stret_fn_addr (LLDB_INVALID_ADDRESS), +    m_msg_forward_addr (LLDB_INVALID_ADDRESS) +{ +    if (process_sp) +        m_process_wp = process_sp; +    // Look up the known resolution functions: +     +    ConstString get_impl_name("class_getMethodImplementation"); +    ConstString get_impl_stret_name("class_getMethodImplementation_stret"); +    ConstString msg_forward_name("_objc_msgForward"); +    ConstString msg_forward_stret_name("_objc_msgForward_stret"); +     +    Target *target = process_sp ? &process_sp->GetTarget() : NULL; +    const Symbol *class_getMethodImplementation = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_name, eSymbolTypeCode); +    const Symbol *class_getMethodImplementation_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_stret_name, eSymbolTypeCode); +    const Symbol *msg_forward = m_objc_module_sp->FindFirstSymbolWithNameAndType (msg_forward_name, eSymbolTypeCode); +    const Symbol *msg_forward_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (msg_forward_stret_name, eSymbolTypeCode); +     +    if (class_getMethodImplementation) +        m_impl_fn_addr = class_getMethodImplementation->GetAddress().GetOpcodeLoadAddress (target); +    if  (class_getMethodImplementation_stret) +        m_impl_stret_fn_addr = class_getMethodImplementation_stret->GetAddress().GetOpcodeLoadAddress (target); +    if (msg_forward) +        m_msg_forward_addr = msg_forward->GetAddress().GetOpcodeLoadAddress(target); +    if  (msg_forward_stret) +        m_msg_forward_stret_addr = msg_forward_stret->GetAddress().GetOpcodeLoadAddress(target); +     +    // FIXME: Do some kind of logging here. +    if (m_impl_fn_addr == LLDB_INVALID_ADDRESS) +    { +        // If we can't even find the ordinary get method implementation function, then we aren't going to be able to +        // step through any method dispatches.  Warn to that effect and get out of here. +        if (process_sp->CanJIT()) +        { +            process_sp->GetTarget().GetDebugger().GetErrorFile()->Printf ("Could not find implementation lookup function \"%s\"" +                                                                          " step in through ObjC method dispatch will not work.\n", +                                                                          get_impl_name.AsCString()); +        } +        return; +    } +    else if (m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS) +    { +        // It there is no stret return lookup function, assume that it is the same as the straight lookup: +        m_impl_stret_fn_addr = m_impl_fn_addr; +        // Also we will use the version of the lookup code that doesn't rely on the stret version of the function. +        g_lookup_implementation_function_code = g_lookup_implementation_no_stret_function_code; +    } +    else +    { +        g_lookup_implementation_function_code = g_lookup_implementation_with_stret_function_code; +    } +         +    // Look up the addresses for the objc dispatch functions and cache them.  For now I'm inspecting the symbol +    // names dynamically to figure out how to dispatch to them.  If it becomes more complicated than this we can  +    // turn the g_dispatch_functions char * array into a template table, and populate the DispatchFunction map +    // from there. + +    for (size_t i = 0; i != llvm::array_lengthof(g_dispatch_functions); i++) +    { +        ConstString name_const_str(g_dispatch_functions[i].name); +        const Symbol *msgSend_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (name_const_str, eSymbolTypeCode); +        if (msgSend_symbol && msgSend_symbol->ValueIsAddress()) +        { +            // FixMe: Make g_dispatch_functions static table of DispatchFunctions, and have the map be address->index. +            // Problem is we also need to lookup the dispatch function.  For now we could have a side table of stret & non-stret +            // dispatch functions.  If that's as complex as it gets, we're fine. +             +            lldb::addr_t sym_addr = msgSend_symbol->GetAddressRef().GetOpcodeLoadAddress(target); +             +            m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i)); +        } +    } +     +    // Build our vtable dispatch handler here: +    m_vtables_ap.reset(new AppleObjCVTables(process_sp, m_objc_module_sp)); +    if (m_vtables_ap.get()) +        m_vtables_ap->ReadRegions();         +} + +lldb::addr_t +AppleObjCTrampolineHandler::SetupDispatchFunction (Thread &thread, ValueList &dispatch_values) +{ +    ExecutionContext exe_ctx (thread.shared_from_this()); +    StreamString errors; +    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); +    lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; +    FunctionCaller *impl_function_caller = nullptr; + +    // Scope for mutex locker: +    { +        Mutex::Locker locker(m_impl_function_mutex); +         +        // First stage is to make the ClangUtility to hold our injected function: + +        if (!m_impl_code.get()) +        { +            if (g_lookup_implementation_function_code != NULL) +            { +                Error error; +                m_impl_code.reset (exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage (g_lookup_implementation_function_code, +                                                                                         eLanguageTypeObjC, +                                                                                         g_lookup_implementation_function_name, +                                                                                         error)); +                if (error.Fail()) +                { +                    if (log) +                        log->Printf ("Failed to get Utility Function for implementation lookup: %s.", error.AsCString()); +                    m_impl_code.reset(); +                    return args_addr; +                } +                 +                if (!m_impl_code->Install(errors, exe_ctx)) +                { +                    if (log) +                        log->Printf ("Failed to install implementation lookup: %s.", errors.GetData()); +                    m_impl_code.reset(); +                    return args_addr; +                } +            } +            else +            { +                if (log) +                    log->Printf("No method lookup implementation code."); +                errors.Printf ("No method lookup implementation code found."); +                return LLDB_INVALID_ADDRESS; +            } +             + +            // Next make the runner function for our implementation utility function. +            ClangASTContext *clang_ast_context = thread.GetProcess()->GetTarget().GetScratchClangASTContext(); +            CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); +            Error error; +             +            impl_function_caller = m_impl_code->MakeFunctionCaller(clang_void_ptr_type, +                                                                   dispatch_values, +                                                                   error); +            if (error.Fail()) +            { +                if (log) +                    log->Printf ("Error getting function caller for dispatch lookup: \"%s\".", error.AsCString()); +                return args_addr; +            } +        } +        else +        { +            impl_function_caller = m_impl_code->GetFunctionCaller(); +        } +    } +     +    errors.Clear(); +     +    // Now write down the argument values for this particular call.  This looks like it might be a race condition +    // if other threads were calling into here, but actually it isn't because we allocate a new args structure for +    // this call by passing args_addr = LLDB_INVALID_ADDRESS... + +    if (impl_function_caller->WriteFunctionArguments (exe_ctx, args_addr, dispatch_values, errors)) +    { +        if (log) +            log->Printf ("Error writing function arguments: \"%s\".", errors.GetData()); +        return args_addr; +    } +         +    return args_addr; +} + +ThreadPlanSP +AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool stop_others) +{ +    ThreadPlanSP ret_plan_sp; +    lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); +     +    DispatchFunction this_dispatch; +    bool found_it = false; +     +    // First step is to look and see if we are in one of the known ObjC dispatch functions.  We've already compiled +    // a table of same, so consult it. +     +    MsgsendMap::iterator pos; +    pos = m_msgSend_map.find (curr_pc); +    if (pos != m_msgSend_map.end()) +    { +        this_dispatch = g_dispatch_functions[(*pos).second]; +        found_it = true; +    } +     +    // Next check to see if we are in a vtable region: +     +    if (!found_it) +    { +        uint32_t flags; +        if (m_vtables_ap.get()) +        { +            found_it = m_vtables_ap->IsAddressInVTables (curr_pc, flags); +            if (found_it) +            { +                this_dispatch.name = "vtable"; +                this_dispatch.stret_return  +                        = (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) == AppleObjCVTables::eOBJC_TRAMPOLINE_STRET; +                this_dispatch.is_super = false; +                this_dispatch.is_super2 = false; +                this_dispatch.fixedup = DispatchFunction::eFixUpFixed; +            } +        } +    } +     +    if (found_it) +    { +        Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + +        // We are decoding a method dispatch.   +        // First job is to pull the arguments out: +         +        lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); +         +        const ABI *abi = NULL; +        ProcessSP process_sp (thread.CalculateProcess()); +        if (process_sp) +            abi = process_sp->GetABI().get(); +        if (abi == NULL) +            return ret_plan_sp; +             +        TargetSP target_sp (thread.CalculateTarget()); +         +        ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext(); +        ValueList argument_values; +        Value void_ptr_value; +        CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); +        void_ptr_value.SetValueType (Value::eValueTypeScalar); +        //void_ptr_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type); +        void_ptr_value.SetCompilerType (clang_void_ptr_type); +         +        int obj_index; +        int sel_index; +         +        // If this is a struct return dispatch, then the first argument is the +        // return struct pointer, and the object is the second, and the selector is the third. +        // Otherwise the object is the first and the selector the second. +        if (this_dispatch.stret_return) +        { +            obj_index = 1; +            sel_index = 2; +            argument_values.PushValue(void_ptr_value); +            argument_values.PushValue(void_ptr_value); +            argument_values.PushValue(void_ptr_value); +        } +        else +        { +            obj_index = 0; +            sel_index = 1; +            argument_values.PushValue(void_ptr_value); +            argument_values.PushValue(void_ptr_value); +        } + +         +        bool success = abi->GetArgumentValues (thread, argument_values); +        if (!success) +            return ret_plan_sp; +         +        lldb::addr_t obj_addr = argument_values.GetValueAtIndex(obj_index)->GetScalar().ULongLong(); +        if (obj_addr == 0x0) +        { +            if (log) +                log->Printf("Asked to step to dispatch to nil object, returning empty plan."); +            return ret_plan_sp; +        } +         +        ExecutionContext exe_ctx (thread.shared_from_this()); +        Process *process = exe_ctx.GetProcessPtr(); +        // isa_addr will store the class pointer that the method is being dispatched to - so either the class +        // directly or the super class if this is one of the objc_msgSendSuper flavors.  That's mostly used to +        // look up the class/selector pair in our cache. +         +        lldb::addr_t isa_addr = LLDB_INVALID_ADDRESS; +        lldb::addr_t sel_addr = argument_values.GetValueAtIndex(sel_index)->GetScalar().ULongLong(); +          +        // Figure out the class this is being dispatched to and see if we've already cached this method call, +        // If so we can push a run-to-address plan directly.  Otherwise we have to figure out where +        // the implementation lives. + +        if (this_dispatch.is_super) +        { +            if (this_dispatch.is_super2) +            { +               // In the objc_msgSendSuper2 case, we don't get the object directly, we get a structure containing +               // the object and the class to which the super message is being sent.  So we need to dig the super +               // out of the class and use that. +                +                Value super_value(*(argument_values.GetValueAtIndex(obj_index))); +                super_value.GetScalar() += process->GetAddressByteSize(); +                super_value.ResolveValue (&exe_ctx); +                 +                if (super_value.GetScalar().IsValid()) +                { +                 +                    // isa_value now holds the class pointer.  The second word of the class pointer is the super-class pointer: +                    super_value.GetScalar() += process->GetAddressByteSize(); +                    super_value.ResolveValue (&exe_ctx); +                    if (super_value.GetScalar().IsValid()) +                        isa_addr = super_value.GetScalar().ULongLong(); +                    else +                    { +                       if (log) +                        log->Printf("Failed to extract the super class value from the class in objc_super."); +                    } +                } +                else +                { +                   if (log) +                    log->Printf("Failed to extract the class value from objc_super."); +                } +            } +            else +            { +               // In the objc_msgSendSuper case, we don't get the object directly, we get a two element structure containing +               // the object and the super class to which the super message is being sent.  So the class we want is +               // the second element of this structure. +                +                Value super_value(*(argument_values.GetValueAtIndex(obj_index))); +                super_value.GetScalar() += process->GetAddressByteSize(); +                super_value.ResolveValue (&exe_ctx); +                 +                if (super_value.GetScalar().IsValid()) +                { +                    isa_addr = super_value.GetScalar().ULongLong(); +                } +                else +                { +                   if (log) +                    log->Printf("Failed to extract the class value from objc_super."); +                } +            } +        } +        else +        { +            // In the direct dispatch case, the object->isa is the class pointer we want. +             +            // This is a little cheesy, but since object->isa is the first field, +            // making the object value a load address value and resolving it will get +            // the pointer sized data pointed to by that value... +             +            // Note, it isn't a fatal error not to be able to get the address from the object, since this might +            // be a "tagged pointer" which isn't a real object, but rather some word length encoded dingus. +             +            Value isa_value(*(argument_values.GetValueAtIndex(obj_index))); + +            isa_value.SetValueType(Value::eValueTypeLoadAddress); +            isa_value.ResolveValue(&exe_ctx); +            if (isa_value.GetScalar().IsValid()) +            { +                isa_addr = isa_value.GetScalar().ULongLong(); +            } +            else +            { +               if (log) +                log->Printf("Failed to extract the isa value from object."); +            } + +        }  +         +        // Okay, we've got the address of the class for which we're resolving this, let's see if it's in our cache: +        lldb::addr_t impl_addr = LLDB_INVALID_ADDRESS; +         +        if (isa_addr != LLDB_INVALID_ADDRESS) +        { +            if (log) +            { +                log->Printf("Resolving call for class - 0x%" PRIx64 " and selector - 0x%" PRIx64, +                            isa_addr, sel_addr); +            } +            ObjCLanguageRuntime *objc_runtime = thread.GetProcess()->GetObjCLanguageRuntime (); +            assert(objc_runtime != NULL); +             +            impl_addr = objc_runtime->LookupInMethodCache (isa_addr, sel_addr); +        }                                       +                                                                                                                           +        if (impl_addr != LLDB_INVALID_ADDRESS) +        { +            // Yup, it was in the cache, so we can run to that address directly. +             +            if (log) +                log->Printf ("Found implementation address in cache: 0x%" PRIx64, impl_addr); +                  +            ret_plan_sp.reset (new ThreadPlanRunToAddress (thread, impl_addr, stop_others)); +        } +        else +        { +            // We haven't seen this class/selector pair yet.  Look it up. +            StreamString errors; +            Address impl_code_address; +             +            ValueList dispatch_values; +             +            // We've will inject a little function in the target that takes the object, selector and some flags, +            // and figures out the implementation.  Looks like: +            //      void *__lldb_objc_find_implementation_for_selector (void *object,  +            //                                                          void *sel,  +            //                                                          int is_stret,  +            //                                                          int is_super,  +            //                                                          int is_super2,  +            //                                                          int is_fixup,  +            //                                                          int is_fixed, +            //                                                          int debug) +            // So set up the arguments for that call. +             +            dispatch_values.PushValue (*(argument_values.GetValueAtIndex(obj_index))); +            dispatch_values.PushValue (*(argument_values.GetValueAtIndex(sel_index))); +             +            Value flag_value; +            CompilerType clang_int_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingSint, 32); +            flag_value.SetValueType (Value::eValueTypeScalar); +            //flag_value.SetContext (Value::eContextTypeClangType, clang_int_type); +            flag_value.SetCompilerType (clang_int_type); +             +            if (this_dispatch.stret_return) +                flag_value.GetScalar() = 1; +            else +                flag_value.GetScalar() = 0; +            dispatch_values.PushValue (flag_value); +                     +            if (this_dispatch.is_super) +                flag_value.GetScalar() = 1; +            else +                flag_value.GetScalar() = 0; +            dispatch_values.PushValue (flag_value); +                     +            if (this_dispatch.is_super2) +                flag_value.GetScalar() = 1; +            else +                flag_value.GetScalar() = 0; +            dispatch_values.PushValue (flag_value); +                     +            switch (this_dispatch.fixedup) +            { +              case DispatchFunction::eFixUpNone: +                 flag_value.GetScalar() = 0; +                 dispatch_values.PushValue (flag_value); +                 dispatch_values.PushValue (flag_value); +                 break; +              case DispatchFunction::eFixUpFixed: +                 flag_value.GetScalar() = 1; +                 dispatch_values.PushValue (flag_value); +                 flag_value.GetScalar() = 1; +                 dispatch_values.PushValue (flag_value); +                 break; +              case DispatchFunction::eFixUpToFix: +                 flag_value.GetScalar() = 1; +                 dispatch_values.PushValue (flag_value); +                 flag_value.GetScalar() = 0; +                 dispatch_values.PushValue (flag_value); +                 break; +            } +            if (log && log->GetVerbose()) +                flag_value.GetScalar() = 1; +            else +                flag_value.GetScalar() = 0;  // FIXME - Set to 0 when debugging is done. +            dispatch_values.PushValue (flag_value); +             +             +            // The step through code might have to fill in the cache, so it is not safe to run only one thread. +            // So we override the stop_others value passed in to us here: +            const bool trampoline_stop_others = false; +            ret_plan_sp.reset (new AppleThreadPlanStepThroughObjCTrampoline (thread, +                                                                             this, +                                                                             dispatch_values, +                                                                             isa_addr, +                                                                             sel_addr, +                                                                             trampoline_stop_others)); +            if (log) +            { +                StreamString s; +                ret_plan_sp->GetDescription(&s, eDescriptionLevelFull); +                log->Printf("Using ObjC step plan: %s.\n", s.GetData()); +            } +        } +    } +     +    return ret_plan_sp; +} + +FunctionCaller * +AppleObjCTrampolineHandler::GetLookupImplementationFunctionCaller () +{ +    return m_impl_code->GetFunctionCaller(); +} diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h new file mode 100644 index 0000000000000..42d3461ddfa5a --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h @@ -0,0 +1,209 @@ +//===-- AppleObjCTrampolineHandler.h ----------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_AppleObjCTrampolineHandler_h_ +#define lldb_AppleObjCTrampolineHandler_h_ + +// C Includes +// C++ Includes +#include <map> +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Expression/UtilityFunction.h" + +namespace lldb_private +{ +   +class AppleObjCTrampolineHandler { +public: +    AppleObjCTrampolineHandler (const lldb::ProcessSP &process_sp,  +                                const lldb::ModuleSP &objc_module_sp); +     +    ~AppleObjCTrampolineHandler(); +             +    lldb::ThreadPlanSP +    GetStepThroughDispatchPlan (Thread &thread,  +                                bool stop_others); +     +    FunctionCaller * +    GetLookupImplementationFunctionCaller (); +     +    bool  +    AddrIsMsgForward (lldb::addr_t addr) const +    { +        return (addr == m_msg_forward_addr || addr == m_msg_forward_stret_addr); +    } + +    struct DispatchFunction { +    public: +        typedef enum  +        { +            eFixUpNone, +            eFixUpFixed, +            eFixUpToFix +        } FixUpState; +                 +        const char *name; +        bool stret_return; +        bool is_super; +        bool is_super2; +        FixUpState fixedup; +    }; + +    lldb::addr_t +    SetupDispatchFunction (Thread &thread, ValueList &dispatch_values); + +private: +    static const char *g_lookup_implementation_function_name; +    static const char *g_lookup_implementation_function_code; +    static const char *g_lookup_implementation_with_stret_function_code; +    static const char *g_lookup_implementation_no_stret_function_code; + +    class AppleObjCVTables +    { +    public: +        // These come from objc-gdb.h. +        enum VTableFlags +        { +             eOBJC_TRAMPOLINE_MESSAGE = (1<<0),   // trampoline acts like objc_msgSend                                                            +             eOBJC_TRAMPOLINE_STRET   = (1<<1),   // trampoline is struct-returning                                                               +             eOBJC_TRAMPOLINE_VTABLE  = (1<<2)    // trampoline is vtable dispatcher                                                              +        }; +             +    private: +        struct VTableDescriptor  +        { +            VTableDescriptor(uint32_t in_flags, lldb::addr_t in_code_start) : +                flags(in_flags), +                code_start(in_code_start) {} +             +            uint32_t flags; +            lldb::addr_t code_start; +        }; + +        class VTableRegion  +        { +        public: +            VTableRegion() : +                    m_valid (false), +                    m_owner (NULL), +                    m_header_addr (LLDB_INVALID_ADDRESS), +                    m_code_start_addr(0), +                    m_code_end_addr (0), +                    m_next_region (0) +            {} +             +            VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr); +             +            void SetUpRegion(); +                         +            lldb::addr_t GetNextRegionAddr () +            { +                return m_next_region; +            } +             +            lldb::addr_t +            GetCodeStart () +            { +                return m_code_start_addr; +            } +             +            lldb::addr_t +            GetCodeEnd () +            { +                return m_code_end_addr; +            } +             +            uint32_t +            GetFlagsForVTableAtAddress (lldb::addr_t address) +            { +                return 0; +            } +             +            bool +            IsValid () +            { +                return m_valid; +            } +             +            bool  +            AddressInRegion (lldb::addr_t addr, uint32_t &flags); +             +            void +            Dump (Stream &s); +             +        public: +            bool m_valid; +            AppleObjCVTables *m_owner; +            lldb::addr_t m_header_addr; +            lldb::addr_t m_code_start_addr; +            lldb::addr_t m_code_end_addr; +            std::vector<VTableDescriptor> m_descriptors; +            lldb::addr_t m_next_region; +        }; +         +    public: +        AppleObjCVTables(const lldb::ProcessSP &process_sp,  +                         const lldb::ModuleSP &objc_module_sp); +         +        ~AppleObjCVTables(); +                 +        bool +        InitializeVTableSymbols (); +                 +        static bool RefreshTrampolines (void *baton,  +                                        StoppointCallbackContext *context,  +                                        lldb::user_id_t break_id,  +                                        lldb::user_id_t break_loc_id); +        bool +        ReadRegions (); +         +        bool +        ReadRegions (lldb::addr_t region_addr); +                 +        bool +        IsAddressInVTables (lldb::addr_t addr, uint32_t &flags); +                 +        lldb::ProcessSP +        GetProcessSP () +        {    +            return m_process_wp.lock(); +        } +         +    private: +        lldb::ProcessWP m_process_wp; +        typedef std::vector<VTableRegion> region_collection; +        lldb::addr_t m_trampoline_header; +        lldb::break_id_t m_trampolines_changed_bp_id; +        region_collection m_regions; +        lldb::ModuleSP m_objc_module_sp; +    }; +     +    static const DispatchFunction g_dispatch_functions[]; +     +    typedef std::map<lldb::addr_t, int> MsgsendMap; // This table maps an dispatch fn address to the index in g_dispatch_functions +    MsgsendMap m_msgSend_map; +    lldb::ProcessWP m_process_wp; +    lldb::ModuleSP m_objc_module_sp; +    std::unique_ptr<UtilityFunction> m_impl_code; +    Mutex m_impl_function_mutex; +    lldb::addr_t m_impl_fn_addr; +    lldb::addr_t m_impl_stret_fn_addr; +    lldb::addr_t m_msg_forward_addr; +    lldb::addr_t m_msg_forward_stret_addr; +    std::unique_ptr<AppleObjCVTables> m_vtables_ap; +}; + +} // namespace lldb_private + +#endif // lldb_AppleObjCTrampolineHandler_h_ diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp new file mode 100644 index 0000000000000..9308c7a668d27 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp @@ -0,0 +1,398 @@ +//===-- AppleObjCTypeEncodingParser.cpp -------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AppleObjCTypeEncodingParser.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/StringLexer.h" + +#include <vector> + +using namespace lldb_private; +using namespace lldb_utility; + +AppleObjCTypeEncodingParser::AppleObjCTypeEncodingParser (ObjCLanguageRuntime& runtime) : +    ObjCLanguageRuntime::EncodingToType(), +    m_runtime(runtime) +{ +    if (!m_scratch_ast_ctx_ap) +        m_scratch_ast_ctx_ap.reset(new ClangASTContext(runtime.GetProcess()->GetTarget().GetArchitecture().GetTriple().str().c_str())); +} + +std::string +AppleObjCTypeEncodingParser::ReadStructName(lldb_utility::StringLexer& type) +{ +    StreamString buffer; +    while (type.HasAtLeast(1) && type.Peek() != '=') +        buffer.Printf("%c",type.Next()); +    return buffer.GetString(); +} + +std::string +AppleObjCTypeEncodingParser::ReadQuotedString(lldb_utility::StringLexer& type) +{ +    StreamString buffer; +    while (type.HasAtLeast(1) && type.Peek() != '"') +        buffer.Printf("%c",type.Next()); +    StringLexer::Character next = type.Next(); +    UNUSED_IF_ASSERT_DISABLED(next); +    assert (next == '"'); +    return buffer.GetString(); +} + +uint32_t +AppleObjCTypeEncodingParser::ReadNumber (lldb_utility::StringLexer& type) +{ +    uint32_t total = 0; +    while (type.HasAtLeast(1) && isdigit(type.Peek())) +           total = 10*total + (type.Next() - '0'); +    return total; +} + +// as an extension to the published grammar recent runtimes emit structs like this: +// "{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}" + +AppleObjCTypeEncodingParser::StructElement::StructElement() : +name(""), +type(clang::QualType()), +bitfield(0) +{} + +AppleObjCTypeEncodingParser::StructElement +AppleObjCTypeEncodingParser::ReadStructElement (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression) +{ +    StructElement retval; +    if (type.NextIf('"')) +        retval.name = ReadQuotedString(type); +    if (!type.NextIf('"')) +        return retval; +    uint32_t bitfield_size = 0; +    retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size); +    retval.bitfield = bitfield_size; +    return retval; +} + +clang::QualType +AppleObjCTypeEncodingParser::BuildStruct (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression) +{ +    return BuildAggregate(ast_ctx, type, for_expression, '{', '}', clang::TTK_Struct); +} + +clang::QualType +AppleObjCTypeEncodingParser::BuildUnion (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression) +{ +    return BuildAggregate(ast_ctx, type, for_expression, '(', ')', clang::TTK_Union); +} + +clang::QualType +AppleObjCTypeEncodingParser::BuildAggregate (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression, char opener, char closer, uint32_t kind) +{ +    if (!type.NextIf(opener)) +        return clang::QualType(); +    std::string name(ReadStructName(type)); +     +    // We do not handle templated classes/structs at the moment. +    // If the name has a < in it, we are going to abandon this. +    // We're still obliged to parse it, so we just set a flag that +    // means "Don't actually build anything." +     +    const bool is_templated = name.find('<') != std::string::npos; +     +    if (!type.NextIf('=')) +        return clang::QualType(); +    bool in_union = true; +    std::vector<StructElement> elements; +    while (in_union && type.HasAtLeast(1)) +    { +        if (type.NextIf(closer)) +        { +            in_union = false; +            break; +        } +        else +        { +            auto element = ReadStructElement(ast_ctx, type, for_expression); +            if (element.type.isNull()) +                break; +            else +                elements.push_back(element); +        } +    } +    if (in_union) +        return clang::QualType(); +     +    if (is_templated) +        return clang::QualType(); // This is where we bail out.  Sorry! +     +    ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx); +    if (!lldb_ctx) +        return clang::QualType(); +    CompilerType union_type(lldb_ctx->CreateRecordType(nullptr, lldb::eAccessPublic, name.c_str(), kind, lldb::eLanguageTypeC)); +    if (union_type) +    { +        ClangASTContext::StartTagDeclarationDefinition(union_type); +         +        unsigned int count = 0; +        for (auto element: elements) +        { +            if (element.name.empty()) +            { +                StreamString elem_name; +                elem_name.Printf("__unnamed_%u",count); +                element.name = std::string(elem_name.GetData()); +            } +            ClangASTContext::AddFieldToRecordType(union_type, element.name.c_str(), CompilerType(&ast_ctx, element.type), lldb::eAccessPublic, element.bitfield); +            ++count; +        } +        ClangASTContext::CompleteTagDeclarationDefinition(union_type); +    } +    return ClangASTContext::GetQualType(union_type); +} + +clang::QualType +AppleObjCTypeEncodingParser::BuildArray (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression) +{ +    if (!type.NextIf('[')) +        return clang::QualType(); +    uint32_t size = ReadNumber(type); +    clang::QualType element_type(BuildType(ast_ctx, type, for_expression)); +    if (!type.NextIf(']')) +        return clang::QualType(); +    ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx); +    if (!lldb_ctx) +        return clang::QualType(); +    CompilerType array_type(lldb_ctx->CreateArrayType(CompilerType(&ast_ctx, element_type), size, false)); +    return ClangASTContext::GetQualType(array_type); +} + +// the runtime can emit these in the form of @"SomeType", giving more specifics +// this would be interesting for expression parser interop, but since we actually try +// to avoid exposing the ivar info to the expression evaluator, consume but ignore the type info +// and always return an 'id'; if anything, dynamic typing will resolve things for us anyway +clang::QualType +AppleObjCTypeEncodingParser::BuildObjCObjectPointerType (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression) +{ +    if (!type.NextIf('@')) +        return clang::QualType(); +     +    std::string name; +     +    if (type.NextIf('"')) +    { +        // We have to be careful here.  We're used to seeing +        //   @"NSString" +        // but in records it is possible that the string following an @ is the name of the next field and @ means "id". +        // This is the case if anything unquoted except for "}", the end of the type, or another name follows the quoted string. +        // +        // E.g. +        // - @"NSString"@ means "id, followed by a field named NSString of type id" +        // - @"NSString"} means "a pointer to NSString and the end of the struct" +        // - @"NSString""nextField" means "a pointer to NSString and a field named nextField" +        // - @"NSString" followed by the end of the string means "a pointer to NSString" +        // +        // As a result, the rule is: If we see @ followed by a quoted string, we peek. +        // - If we see }, ), ], the end of the string, or a quote ("), the quoted string is a class name. +        // - If we see anything else, the quoted string is a field name and we push it back onto type. + +        name = ReadQuotedString(type); +         +        if (type.HasAtLeast(1)) +        { +            switch (type.Peek()) +            { +            default: +                // roll back +                type.PutBack(name.length() + 2); // undo our consumption of the string and of the quotes +                name.clear(); +                break; +            case '}': +            case ')': +            case ']': +            case '"': +                // the quoted string is a class name – see the rule +                break; +            } +        } +        else +        { +            // the quoted string is a class name – see the rule +        } +    } +     +    if (for_expression && !name.empty()) +    { +        size_t less_than_pos = name.find('<'); +         +        if (less_than_pos != std::string::npos) +        { +            if (less_than_pos == 0) +                return ast_ctx.getObjCIdType(); +            else +                name.erase(less_than_pos); +        } +         +        DeclVendor *decl_vendor = m_runtime.GetDeclVendor(); +         +        assert (decl_vendor); // how are we parsing type encodings for expressions if a type vendor isn't in play? +         +        const bool append = false; +        const uint32_t max_matches = 1; +        std::vector<clang::NamedDecl *> decls; +         +        uint32_t num_types = decl_vendor->FindDecls(ConstString(name), +                                                    append, +                                                    max_matches, +                                                    decls); + +        // The user can forward-declare something that has no definition.  The runtime doesn't prohibit this at all. +        // This is a rare and very weird case.  We keep this assert in debug builds so we catch other weird cases. +#ifdef LLDB_CONFIGURATION_DEBUG +        assert(num_types); +#else +        if (!num_types) +            return ast_ctx.getObjCIdType(); +#endif +         +        return ClangASTContext::GetQualType(ClangASTContext::GetTypeForDecl(decls[0]).GetPointerType()); +    } +    else +    { +        // We're going to resolve this dynamically anyway, so just smile and wave. +        return ast_ctx.getObjCIdType(); +    } +} + +clang::QualType +AppleObjCTypeEncodingParser::BuildType (clang::ASTContext &ast_ctx, StringLexer& type, bool for_expression, uint32_t *bitfield_bit_size) +{ +    if (!type.HasAtLeast(1)) +        return clang::QualType(); +     +    switch (type.Peek()) +    { +    default: +        break; +    case '{': +        return BuildStruct(ast_ctx, type, for_expression); +    case '[': +        return BuildArray(ast_ctx, type, for_expression); +    case '(': +        return BuildUnion(ast_ctx, type, for_expression); +    case '@': +        return BuildObjCObjectPointerType(ast_ctx, type, for_expression); +    } +     +    switch (type.Next()) +    { +    default: +        type.PutBack(1); +        return clang::QualType(); +    case 'c': +        return ast_ctx.CharTy; +    case 'i': +        return ast_ctx.IntTy; +    case 's': +        return ast_ctx.ShortTy; +    case 'l': +        return ast_ctx.getIntTypeForBitwidth(32, true); +        // this used to be done like this: +        //   ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx); +        //   if (!lldb_ctx) +        //      return clang::QualType(); +        //   return lldb_ctx->GetIntTypeFromBitSize(32, true).GetQualType(); +        // which uses one of the constants if one is available, but we don't think all this work is necessary. +    case 'q': +        return ast_ctx.LongLongTy; +    case 'C': +        return ast_ctx.UnsignedCharTy; +    case 'I': +        return ast_ctx.UnsignedIntTy; +    case 'S': +        return ast_ctx.UnsignedShortTy; +    case 'L': +        return ast_ctx.getIntTypeForBitwidth(32, false); +        // see note for 'l' +    case 'Q': +        return ast_ctx.UnsignedLongLongTy; +    case 'f': +        return ast_ctx.FloatTy; +    case 'd': +        return ast_ctx.DoubleTy; +    case 'B': +        return ast_ctx.BoolTy; +    case 'v': +        return ast_ctx.VoidTy; +    case '*': +        return ast_ctx.getPointerType(ast_ctx.CharTy); +    case '#': +        return ast_ctx.getObjCClassType(); +    case ':': +        return ast_ctx.getObjCSelType(); +    case 'b': +        { +            uint32_t size = ReadNumber(type); +            if (bitfield_bit_size) +            { +                *bitfield_bit_size = size; +                return ast_ctx.UnsignedIntTy; // FIXME: the spec is fairly vague here. +            } +            else +                return clang::QualType(); +        } +    case 'r': +        { +            clang::QualType target_type = BuildType(ast_ctx, type, for_expression); +            if (target_type.isNull()) +                return clang::QualType(); +            else if (target_type == ast_ctx.UnknownAnyTy) +                return ast_ctx.UnknownAnyTy; +            else +                return ast_ctx.getConstType(target_type); +        } +    case '^': +        { +            if (!for_expression && type.NextIf('?')) +            { +                // if we are not supporting the concept of unknownAny, but what is being created here is an unknownAny*, then +                // we can just get away with a void* +                // this is theoretically wrong (in the same sense as 'theoretically nothing exists') but is way better than outright failure +                // in many practical cases +                return ast_ctx.VoidPtrTy; +            } +            else +            { +                clang::QualType target_type = BuildType(ast_ctx, type, for_expression); +                if (target_type.isNull()) +                    return clang::QualType(); +                else if (target_type == ast_ctx.UnknownAnyTy) +                    return ast_ctx.UnknownAnyTy; +                else +                    return ast_ctx.getPointerType(target_type); +            } +        } +    case '?': +        return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType(); +    } +} + +CompilerType +AppleObjCTypeEncodingParser::RealizeType (clang::ASTContext &ast_ctx, const char* name, bool for_expression) +{ +    if (name && name[0]) +    { +        StringLexer lexer(name); +        clang::QualType qual_type = BuildType(ast_ctx, lexer, for_expression); +        return CompilerType(&ast_ctx, qual_type); +    } +    return CompilerType(); +} + diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h new file mode 100644 index 0000000000000..87c49cbc05b92 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h @@ -0,0 +1,81 @@ +//===-- AppleObjCTypeEncodingParser.h ---------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AppleObjCTypeEncodingParser_h_ +#define liblldb_AppleObjCTypeEncodingParser_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "clang/AST/ASTContext.h" + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ObjCLanguageRuntime.h" + +namespace lldb_utility { +    class StringLexer; +} + +namespace lldb_private { + +    class AppleObjCTypeEncodingParser : public ObjCLanguageRuntime::EncodingToType +    { +    public: +        AppleObjCTypeEncodingParser (ObjCLanguageRuntime& runtime); +        ~AppleObjCTypeEncodingParser() override = default; +         +        CompilerType RealizeType(clang::ASTContext &ast_ctx, const char* name, bool for_expression) override; + +    private: +        struct StructElement { +            std::string name; +            clang::QualType type; +            uint32_t bitfield; +             +            StructElement (); +            ~StructElement () = default; +        }; +         +        clang::QualType +        BuildType (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression, uint32_t *bitfield_bit_size = nullptr); + +        clang::QualType +        BuildStruct (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression); +         +        clang::QualType +        BuildAggregate (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression, char opener, char closer, uint32_t kind); +         +        clang::QualType +        BuildUnion (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression); +         +        clang::QualType +        BuildArray (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression); +         +        std::string +        ReadStructName(lldb_utility::StringLexer& type); +         +        StructElement +        ReadStructElement (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression); +         +        clang::QualType +        BuildObjCObjectPointerType (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression); +         +        uint32_t +        ReadNumber (lldb_utility::StringLexer& type); +         +        std::string +        ReadQuotedString(lldb_utility::StringLexer& type); +         +        ObjCLanguageRuntime& m_runtime; +    }; +     +} // namespace lldb_private + +#endif // liblldb_AppleObjCTypeEncodingParser_h_ diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp new file mode 100644 index 0000000000000..285786a09dbbd --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp @@ -0,0 +1,244 @@ +//===-- AppleThreadPlanStepThroughObjCTrampoline.cpp --------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "AppleThreadPlanStepThroughObjCTrampoline.h" +#include "AppleObjCTrampolineHandler.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Core/Log.h" + + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepThroughObjCTrampoline constructor +//---------------------------------------------------------------------- +AppleThreadPlanStepThroughObjCTrampoline::AppleThreadPlanStepThroughObjCTrampoline +( +    Thread &thread,  +    AppleObjCTrampolineHandler *trampoline_handler, +    ValueList &input_values, +    lldb::addr_t isa_addr, +    lldb::addr_t sel_addr, +    bool stop_others +) : +    ThreadPlan (ThreadPlan::eKindGeneric,  +                "MacOSX Step through ObjC Trampoline",  +                thread,  +                eVoteNoOpinion,  +                eVoteNoOpinion), +    m_trampoline_handler (trampoline_handler), +    m_args_addr (LLDB_INVALID_ADDRESS), +    m_input_values (input_values), +    m_isa_addr(isa_addr), +    m_sel_addr(sel_addr), +    m_impl_function (NULL), +    m_stop_others (stop_others) +{ +     +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +AppleThreadPlanStepThroughObjCTrampoline::~AppleThreadPlanStepThroughObjCTrampoline() +{ +} + +void +AppleThreadPlanStepThroughObjCTrampoline::DidPush () +{ +    // Setting up the memory space for the called function text might require allocations, +    // i.e. a nested function call.  This needs to be done as a PreResumeAction. +    m_thread.GetProcess()->AddPreResumeAction (PreResumeInitializeFunctionCaller, (void *) this); +} + +bool +AppleThreadPlanStepThroughObjCTrampoline::InitializeFunctionCaller () +{ +    if (!m_func_sp) +    { +        StreamString errors; +        m_args_addr = m_trampoline_handler->SetupDispatchFunction(m_thread, m_input_values); +         +        if (m_args_addr == LLDB_INVALID_ADDRESS) +        { +            return false; +        } +        m_impl_function = m_trampoline_handler->GetLookupImplementationFunctionCaller(); +        ExecutionContext exc_ctx; +        EvaluateExpressionOptions options; +        options.SetUnwindOnError(true); +        options.SetIgnoreBreakpoints(true); +        options.SetStopOthers(m_stop_others); +        m_thread.CalculateExecutionContext(exc_ctx); +        m_func_sp = m_impl_function->GetThreadPlanToCallFunction (exc_ctx, +                                                                  m_args_addr, +                                                                  options, +                                                                  errors); +        m_func_sp->SetOkayToDiscard(true); +        m_thread.QueueThreadPlan (m_func_sp, false); +    } +    return true; +} + +bool +AppleThreadPlanStepThroughObjCTrampoline::PreResumeInitializeFunctionCaller(void *void_myself) +{ +    AppleThreadPlanStepThroughObjCTrampoline *myself = static_cast<AppleThreadPlanStepThroughObjCTrampoline *>(void_myself); +    return myself->InitializeFunctionCaller(); +} + +void +AppleThreadPlanStepThroughObjCTrampoline::GetDescription (Stream *s, +                                                          lldb::DescriptionLevel level) +{ +    if (level == lldb::eDescriptionLevelBrief) +        s->Printf("Step through ObjC trampoline"); +    else +    { +        s->Printf ("Stepping to implementation of ObjC method - obj: 0x%llx, isa: 0x%" PRIx64 ", sel: 0x%" PRIx64, +                   m_input_values.GetValueAtIndex(0)->GetScalar().ULongLong(), m_isa_addr, m_sel_addr); +    } +} +                 +bool +AppleThreadPlanStepThroughObjCTrampoline::ValidatePlan (Stream *error) +{ +    return true; +} + +bool +AppleThreadPlanStepThroughObjCTrampoline::DoPlanExplainsStop (Event *event_ptr) +{ +    // If we get asked to explain the stop it will be because something went +    // wrong (like the implementation for selector function crashed...  We're going +    // to figure out what to do about that, so we do explain the stop. +    return true; +} + +lldb::StateType +AppleThreadPlanStepThroughObjCTrampoline::GetPlanRunState () +{ +    return eStateRunning; +} + +bool +AppleThreadPlanStepThroughObjCTrampoline::ShouldStop (Event *event_ptr) +{ +    // First stage: we are still handling the "call a function to get the target of the dispatch" +    if (m_func_sp) +    { +        if (!m_func_sp->IsPlanComplete()) +        { +            return false; +        } +        else +        { +            if (!m_func_sp->PlanSucceeded()) +            { +                SetPlanComplete(false); +                return true; +            } +            m_func_sp.reset(); +        } +    } +     +    // Second stage, if all went well with the function calling, then fetch the target address, and +    // queue up a "run to that address" plan. +    if (!m_run_to_sp)  +    { +        Value target_addr_value; +        ExecutionContext exc_ctx; +        m_thread.CalculateExecutionContext(exc_ctx); +        m_impl_function->FetchFunctionResults (exc_ctx, m_args_addr, target_addr_value); +        m_impl_function->DeallocateFunctionResults(exc_ctx, m_args_addr); +        lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong(); +        Address target_so_addr; +        target_so_addr.SetOpcodeLoadAddress(target_addr, exc_ctx.GetTargetPtr()); +        Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); +        if (target_addr == 0) +        { +            if (log) +                log->Printf("Got target implementation of 0x0, stopping."); +            SetPlanComplete(); +            return true; +        } +        if (m_trampoline_handler->AddrIsMsgForward(target_addr)) +        { +            if (log) +                log->Printf ("Implementation lookup returned msgForward function: 0x%" PRIx64 ", stopping.", target_addr); + +            SymbolContext sc = m_thread.GetStackFrameAtIndex(0)->GetSymbolContext(eSymbolContextEverything); +            const bool abort_other_plans = false; +            const bool first_insn = true; +            const uint32_t frame_idx = 0; +            m_run_to_sp = m_thread.QueueThreadPlanForStepOutNoShouldStop (abort_other_plans, +                                           &sc, +                                           first_insn, +                                           m_stop_others, +                                           eVoteNoOpinion, +                                           eVoteNoOpinion, +                                           frame_idx); +            m_run_to_sp->SetPrivate(true); +            return false; +        } +         +        if (log) +            log->Printf("Running to ObjC method implementation: 0x%" PRIx64, target_addr); +         +        ObjCLanguageRuntime *objc_runtime = GetThread().GetProcess()->GetObjCLanguageRuntime(); +        assert (objc_runtime != NULL); +        objc_runtime->AddToMethodCache (m_isa_addr, m_sel_addr, target_addr); +        if (log) +            log->Printf("Adding {isa-addr=0x%" PRIx64 ", sel-addr=0x%" PRIx64 "} = addr=0x%" PRIx64 " to cache.", m_isa_addr, m_sel_addr, target_addr); + +        // Extract the target address from the value: +         +        m_run_to_sp.reset(new ThreadPlanRunToAddress(m_thread, target_so_addr, m_stop_others)); +        m_thread.QueueThreadPlan(m_run_to_sp, false); +        m_run_to_sp->SetPrivate(true); +        return false; +    } +    else if (m_thread.IsThreadPlanDone(m_run_to_sp.get())) +    { +        // Third stage, work the run to target plan. +        SetPlanComplete(); +        return true; +    } +    return false; +} + +// The base class MischiefManaged does some cleanup - so you have to call it +// in your MischiefManaged derived class. +bool +AppleThreadPlanStepThroughObjCTrampoline::MischiefManaged () +{ +    if (IsPlanComplete()) +        return true; +    else +        return false; +} + +bool +AppleThreadPlanStepThroughObjCTrampoline::WillStop() +{ +    return true; +} diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h new file mode 100644 index 0000000000000..8db9963fa51a3 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h @@ -0,0 +1,95 @@ +//===-- AppleThreadPlanStepThroughObjCTrampoline.h --------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_AppleThreadPlanStepThroughObjCTrampoline_h_ +#define lldb_AppleThreadPlanStepThroughObjCTrampoline_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-types.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/Value.h" +#include "lldb/Target/ThreadPlan.h" +#include "AppleObjCTrampolineHandler.h" + +namespace lldb_private  +{ + +class AppleThreadPlanStepThroughObjCTrampoline : public ThreadPlan +{ +public: +    AppleThreadPlanStepThroughObjCTrampoline(Thread &thread,  +                                             AppleObjCTrampolineHandler *trampoline_handler,  +                                             ValueList &values, +                                             lldb::addr_t isa_addr, +                                             lldb::addr_t sel_addr, +                                             bool stop_others); + +    ~AppleThreadPlanStepThroughObjCTrampoline() override; + +    static bool +    PreResumeInitializeFunctionCaller(void *myself); + +    void +    GetDescription(Stream *s, +                   lldb::DescriptionLevel level) override; +                     +    bool +    ValidatePlan(Stream *error) override; + +    lldb::StateType +    GetPlanRunState() override; + +    bool +    ShouldStop(Event *event_ptr) override; +     +    bool +    StopOthers() override +    { +        return m_stop_others; +    } + +    // The base class MischiefManaged does some cleanup - so you have to call it +    // in your MischiefManaged derived class. +    bool +    MischiefManaged() override; +     +    void +    DidPush() override; +     +    bool +    WillStop() override; + +protected: +    bool +    DoPlanExplainsStop(Event *event_ptr) override; +	 +private: +    bool +    InitializeFunctionCaller (); + +    AppleObjCTrampolineHandler *m_trampoline_handler; // FIXME - ensure this doesn't go away on us?  SP maybe? +    lldb::addr_t m_args_addr;     // Stores the address for our step through function result structure. +    //lldb::addr_t m_object_addr;  // This is only for Description. +    ValueList    m_input_values; +    lldb::addr_t m_isa_addr;     // isa_addr and sel_addr are the keys we will use to cache the implementation. +    lldb::addr_t m_sel_addr; +    lldb::ThreadPlanSP m_func_sp;       // This is the function call plan.  We fill it at start, then set it +                                        // to NULL when this plan is done.  That way we know to go to: +    lldb::ThreadPlanSP m_run_to_sp;     // The plan that runs to the target. +    FunctionCaller *m_impl_function;  // This is a pointer to a impl function that +                                     // is owned by the client that pushes this plan. +    bool m_stop_others; +}; + +} // namespace lldb_private + +#endif // lldb_AppleThreadPlanStepThroughObjCTrampoline_h_  | 
