diff options
3 files changed, 546 insertions, 1 deletions
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index 46a663533b7..3a7de482708 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -20,6 +20,7 @@ #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/ClangForward.h" #include "lldb/Core/ConstString.h" +#include "lldb/Core/DataBufferMemoryMap.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" @@ -31,6 +32,7 @@ #include "lldb/Expression/ClangFunction.h" #include "lldb/Expression/ClangUtilityFunction.h" #include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Target/ExecutionContext.h" @@ -627,6 +629,531 @@ AppleObjCRuntimeV2::GetClassDescriptor (ValueObject& in_value) return descriptor; } +class RemoteNXMapTable +{ +public: + RemoteNXMapTable (lldb::ProcessSP process_sp, + lldb::addr_t load_addr) : + m_process_sp(process_sp), + m_end_iterator(*this, -1), + m_load_addr(load_addr), + m_map_pair_size(m_process_sp->GetAddressByteSize() * 2), + m_NXMAPNOTAKEY(m_process_sp->GetAddressByteSize() == 8 ? 0xffffffffffffffffull : 0xffffffffull) + { + lldb::addr_t cursor = load_addr; + + Error err; + + // const struct +NXMapTablePrototype *prototype; + m_prototype_la = m_process_sp->ReadPointerFromMemory(cursor, err); + cursor += m_process_sp->GetAddressByteSize(); + + // unsigned count; + m_count = m_process_sp->ReadUnsignedIntegerFromMemory(cursor, sizeof(unsigned), 0, err); + cursor += sizeof(unsigned); + + // unsigned nbBucketsMinusOne; + m_nbBucketsMinusOne = m_process_sp->ReadUnsignedIntegerFromMemory(cursor, sizeof(unsigned), 0, err); + cursor += sizeof(unsigned); + + // void *buckets; + m_buckets_la = m_process_sp->ReadPointerFromMemory(cursor, err); + } + + // 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_la = m_parent.m_buckets_la; + size_t map_pair_size = m_parent.m_map_pair_size; + lldb::addr_t pair_la = pairs_la + (m_index * map_pair_size); + + Error err; + + lldb::addr_t key = m_parent.m_process_sp->ReadPointerFromMemory(pair_la, err); + if (!err.Success()) + return element(); + lldb::addr_t value = m_parent.m_process_sp->ReadPointerFromMemory(pair_la + m_parent.m_process_sp->GetAddressByteSize(), err); + if (!err.Success()) + return element(); + + std::string key_string; + + m_parent.m_process_sp->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; + + lldb::addr_t pairs_la = m_parent.m_buckets_la; + size_t map_pair_size = m_parent.m_map_pair_size; + lldb::addr_t NXMAPNOTAKEY = m_parent.m_NXMAPNOTAKEY; + Error err; + + while (m_index--) + { + lldb::addr_t pair_la = pairs_la + (m_index * map_pair_size); + lldb::addr_t key = m_parent.m_process_sp->ReadPointerFromMemory(pair_la, err); + + if (!err.Success()) + { + m_index = -1; + return; + } + + if (key != NXMAPNOTAKEY) + return; + } + } + RemoteNXMapTable &m_parent; + int m_index; + }; + + const_iterator begin () + { + return const_iterator(*this, m_nbBucketsMinusOne + 1); + } + + const_iterator end () + { + return m_end_iterator; + } + +private: + // contents of _NXMapTable struct + lldb::addr_t m_prototype_la; + uint32_t m_count; + uint32_t m_nbBucketsMinusOne; + lldb::addr_t m_buckets_la; + + lldb::ProcessSP m_process_sp; + const_iterator m_end_iterator; + lldb::addr_t m_load_addr; + size_t m_map_pair_size; + lldb::addr_t m_NXMAPNOTAKEY; +}; + +class RemoteObjCOpt +{ +public: + RemoteObjCOpt (lldb::ProcessSP process_sp, + lldb::addr_t load_addr) : + m_process_sp(process_sp), + m_end_iterator(*this, -1ll), + m_load_addr(load_addr) + { + lldb::addr_t cursor = load_addr; + + Error err; + + // uint32_t version; + m_version = m_process_sp->ReadUnsignedIntegerFromMemory(cursor, sizeof(uint32_t), 0, err); + cursor += sizeof(uint32_t); + + // int32_t selopt_offset; + cursor += sizeof(int32_t); + + // int32_t headeropt_offset; + cursor += sizeof(int32_t); + + // int32_t clsopt_offset; + { + Scalar clsopt_offset; + m_process_sp->ReadScalarIntegerFromMemory(cursor, sizeof(int32_t), /*is_signed*/ true, clsopt_offset, err); + m_clsopt_offset = clsopt_offset.SInt(); + cursor += sizeof(int32_t); + } + + if (m_version != 12) + return; + + m_clsopt_la = load_addr + m_clsopt_offset; + + cursor = m_clsopt_la; + + // uint32_t capacity; + m_capacity = m_process_sp->ReadUnsignedIntegerFromMemory(cursor, sizeof(uint32_t), 0, err); + cursor += sizeof(uint32_t); + + // uint32_t occupied; + cursor += sizeof(uint32_t); + + // uint32_t shift; + cursor += sizeof(uint32_t); + + // uint32_t mask; + m_mask = m_process_sp->ReadUnsignedIntegerFromMemory(cursor, sizeof(uint32_t), 0, err); + cursor += sizeof(uint32_t); + + // uint32_t zero; + m_zero_offset = cursor - m_clsopt_la; + cursor += sizeof(uint32_t); + + // uint32_t unused; + cursor += sizeof(uint32_t); + + // uint64_t salt; + cursor += sizeof(uint64_t); + + // uint32_t scramble[256]; + cursor += sizeof(uint32_t) * 256; + + // uint8_t tab[mask+1]; + cursor += sizeof(uint8_t) * (m_mask + 1); + + // uint8_t checkbytes[capacity]; + cursor += sizeof(uint8_t) * m_capacity; + + // int32_t offset[capacity]; + cursor += sizeof(int32_t) * m_capacity; + + // objc_classheader_t clsOffsets[capacity]; + m_clsOffsets_la = cursor; + cursor += (m_classheader_size * m_capacity); + + // uint32_t duplicateCount; + m_duplicateCount = m_process_sp->ReadUnsignedIntegerFromMemory(cursor, sizeof(uint32_t), 0, err); + cursor += sizeof(uint32_t); + + // objc_classheader_t duplicateOffsets[duplicateCount]; + m_duplicateOffsets_la = cursor; + } + + friend class const_iterator; + class const_iterator + { + public: + const_iterator (RemoteObjCOpt &parent, int64_t 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) + { + 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 ObjCLanguageRuntime::ObjCISA operator*() const + { + if (m_index == -1) + return 0; + + Error err; + return isaForIndex(err); + } + private: + ObjCLanguageRuntime::ObjCISA isaForIndex(Error &err) const + { + if (m_index >= m_parent.m_capacity + m_parent.m_duplicateCount) + return 0; // index out of range + + lldb::addr_t classheader_la; + + if (m_index >= m_parent.m_capacity) + { + // index in the duplicate offsets + uint32_t index = (uint32_t)((uint64_t)m_index - (uint64_t)m_parent.m_capacity); + classheader_la = m_parent.m_duplicateOffsets_la + (index * m_parent.m_classheader_size); + } + else + { + // index in the offsets + uint32_t index = (uint32_t)m_index; + classheader_la = m_parent.m_clsOffsets_la + (index * m_parent.m_classheader_size); + } + + Scalar clsOffset; + m_parent.m_process_sp->ReadScalarIntegerFromMemory(classheader_la, sizeof(int32_t), /*is_signed*/ true, clsOffset, err); + if (!err.Success()) + return 0; + + int32_t clsOffset_int = clsOffset.SInt(); + if (clsOffset_int & 0x1) + return 0; // not even + + if (clsOffset_int == m_parent.m_zero_offset) + return 0; // == offsetof(objc_clsopt_t, zero) + + return m_parent.m_clsopt_la + (int64_t)clsOffset_int; + } + + void AdvanceToValidIndex () + { + if (m_index == -1) + return; + + Error err; + + m_index--; + + while (m_index >= 0) + { + ObjCLanguageRuntime::ObjCISA objc_isa = isaForIndex(err); + if (objc_isa) + return; + m_index--; + } + } + RemoteObjCOpt &m_parent; + int64_t m_index; + }; + + const_iterator begin () + { + return const_iterator(*this, (int64_t)m_capacity + (int64_t)m_duplicateCount); + } + + const_iterator end () + { + return m_end_iterator; + } + +private: + // contents of objc_opt struct + uint32_t m_version; + int32_t m_clsopt_offset; + + lldb::addr_t m_clsopt_la; + + // contents of objc_clsopt struct + uint32_t m_capacity; + uint32_t m_mask; + uint32_t m_duplicateCount; + lldb::addr_t m_clsOffsets_la; + lldb::addr_t m_duplicateOffsets_la; + int32_t m_zero_offset; + + lldb::ProcessSP m_process_sp; + const_iterator m_end_iterator; + lldb::addr_t m_load_addr; + const size_t m_classheader_size = (sizeof(int32_t) * 2); +}; + +ModuleSP FindLibobjc (Target &target) +{ + ModuleList& modules = target.GetImages(); + for (uint32_t idx = 0; idx < modules.GetSize(); idx++) + { + lldb::ModuleSP module_sp = modules.GetModuleAtIndex(idx); + if (!module_sp) + continue; + if (strncmp(module_sp->GetFileSpec().GetFilename().AsCString(""), "libobjc.", sizeof("libobjc.") - 1) == 0) + return module_sp; + } + + return ModuleSP(); +} + +void +AppleObjCRuntimeV2::UpdateISAToDescriptorMap_Impl() +{ + lldb::LogSP log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + Process *process_ptr = GetProcess(); + + if (!process_ptr) + return; + + ProcessSP process_sp = process_ptr->shared_from_this(); + + Target &target(process_sp->GetTarget()); + + ModuleSP objc_module_sp(FindLibobjc(target)); + + if (!objc_module_sp) + return; + + do + { + SymbolContextList sc_list; + + size_t num_symbols = objc_module_sp->FindSymbolsWithNameAndType(ConstString("gdb_objc_realized_classes"), + lldb::eSymbolTypeData, + sc_list); + + if (!num_symbols) + break; + + SymbolContext gdb_objc_realized_classes_sc; + + if (!sc_list.GetContextAtIndex(0, gdb_objc_realized_classes_sc)) + break; + + AddressRange gdb_objc_realized_classes_addr_range; + + const uint32_t scope = eSymbolContextSymbol; + const uint32_t range_idx = 0; + bool use_inline_block_range = false; + + if (!gdb_objc_realized_classes_sc.GetAddressRange(scope, + range_idx, + use_inline_block_range, + gdb_objc_realized_classes_addr_range)) + break; + + lldb::addr_t gdb_objc_realized_classes_la = gdb_objc_realized_classes_addr_range.GetBaseAddress().GetLoadAddress(&target); + + if (gdb_objc_realized_classes_la == LLDB_INVALID_ADDRESS) + break; + + // <rdar://problem/10763513> + + lldb::addr_t gdb_objc_realized_classes_nxmaptable_la; + + { + Error err; + gdb_objc_realized_classes_nxmaptable_la = process_sp->ReadPointerFromMemory(gdb_objc_realized_classes_la, err); + if (!err.Success()) + break; + } + + RemoteNXMapTable gdb_objc_realized_classes(process_sp, gdb_objc_realized_classes_nxmaptable_la); + + for (RemoteNXMapTable::element elt : gdb_objc_realized_classes) + { + if (m_isa_to_descriptor_cache.count(elt.second)) + continue; + + ClassDescriptorSP descriptor_sp = ClassDescriptorSP(new ClassDescriptorV2(elt.second, process_sp)); + + if (log) + log->Printf("AppleObjCRuntimeV2 added (ObjCISA)0x%llx (%s) from dynamic table to isa->descriptor cache", elt.second, elt.first.AsCString()); + + m_isa_to_descriptor_cache[elt.second] = descriptor_sp; + } + } + while(0); + + do + { + ObjectFile *objc_object = objc_module_sp->GetObjectFile(); + + if (!objc_object) + break; + + SectionList *section_list = objc_object->GetSectionList(); + + if (!section_list) + break; + + SectionSP TEXT_section_sp = section_list->FindSectionByName(ConstString("__TEXT")); + + if (!TEXT_section_sp) + break; + + SectionList &TEXT_children = TEXT_section_sp->GetChildren(); + + SectionSP objc_opt_section_sp = TEXT_children.FindSectionByName(ConstString("__objc_opt_ro")); + + if (!objc_opt_section_sp) + break; + + lldb::addr_t objc_opt_la = objc_opt_section_sp->GetLoadBaseAddress(&target); + + if (objc_opt_la == LLDB_INVALID_ADDRESS) + break; + + RemoteObjCOpt objc_opt(process_sp, objc_opt_la); + + for (ObjCLanguageRuntime::ObjCISA objc_isa : objc_opt) + { + if (m_isa_to_descriptor_cache.count(objc_isa)) + continue; + + ClassDescriptorSP descriptor_sp = ClassDescriptorSP(new ClassDescriptorV2(objc_isa, process_sp)); + + if (log) + log->Printf("AppleObjCRuntimeV2 added (ObjCISA)0x%llx (%s) from static table to isa->descriptor cache", objc_isa, descriptor_sp->GetClassName().AsCString()); + + m_isa_to_descriptor_cache[objc_isa] = descriptor_sp; + } + } + while (0); +} + // this code relies on the assumption that an Objective-C object always starts // with an ISA at offset 0. an ISA is effectively a pointer to an instance of // struct class_t in the ObjCv2 runtime diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h index 6fff35f3012..08b325dcdb2 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h @@ -232,6 +232,9 @@ public: virtual size_t GetByteOffsetForIvar (ClangASTType &parent_qual_type, const char *ivar_name); + virtual void + UpdateISAToDescriptorMap_Impl(); + virtual bool IsValidISA (ObjCLanguageRuntime::ObjCISA isa) { diff --git a/lldb/source/Target/ObjCLanguageRuntime.cpp b/lldb/source/Target/ObjCLanguageRuntime.cpp index df00c353c4a..5bf0a79e86d 100644 --- a/lldb/source/Target/ObjCLanguageRuntime.cpp +++ b/lldb/source/Target/ObjCLanguageRuntime.cpp @@ -30,7 +30,8 @@ ObjCLanguageRuntime::~ObjCLanguageRuntime() ObjCLanguageRuntime::ObjCLanguageRuntime (Process *process) : LanguageRuntime (process), - m_has_new_literals_and_indexing (eLazyBoolCalculate) + m_has_new_literals_and_indexing (eLazyBoolCalculate), + m_isa_to_descriptor_cache_is_up_to_date (false) { } @@ -283,10 +284,24 @@ ObjCLanguageRuntime::ClassDescriptor::IsPointerValid (lldb::addr_t value, ObjCLanguageRuntime::ObjCISA ObjCLanguageRuntime::GetISA(const ConstString &name) { + // Try once regardless of whether the map has been brought up to date. We + // might have encountered the relevant isa directly. + for (const ISAToDescriptorMap::value_type &val : m_isa_to_descriptor_cache) + if (val.second && val.second->GetClassName() == name) + return val.first; + + // If the map is up to date and we didn't find the isa, give up. + if (m_isa_to_descriptor_cache_is_up_to_date) + return 0; + + // Try again after bringing the map up to date. + UpdateISAToDescriptorMap(); + for (const ISAToDescriptorMap::value_type &val : m_isa_to_descriptor_cache) if (val.second && val.second->GetClassName() == name) return val.first; + // Now we know for sure that the class isn't there. Give up. return 0; } |