aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp573
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h411
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp665
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h55
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp541
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h148
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp459
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h207
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp2215
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h336
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp1144
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h209
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp398
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h81
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp244
-rw-r--r--source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h95
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 000000000000..711d324d8aa7
--- /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 000000000000..18591ecd6e93
--- /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 000000000000..cd6ece297ed1
--- /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 000000000000..88789c7b5a8d
--- /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 000000000000..cdb95250b2f4
--- /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 000000000000..342824e79b1f
--- /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 000000000000..59b28b63f6b3
--- /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 000000000000..9f9fcc69b682
--- /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 000000000000..8c485d97bdc9
--- /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 000000000000..96b47e8770f9
--- /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 000000000000..d38a076ad5d7
--- /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 000000000000..42d3461ddfa5
--- /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 000000000000..9308c7a668d2
--- /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 000000000000..87c49cbc05b9
--- /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 000000000000..285786a09dbb
--- /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 000000000000..8db9963fa51a
--- /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_