//===-- ObjCLanguageRuntime.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_ObjCLanguageRuntime_h_ #define liblldb_ObjCLanguageRuntime_h_ // C Includes // C++ Includes #include #include #include #include // Other libraries and framework includes #include "llvm/Support/Casting.h" // Project includes #include "lldb/Core/PluginInterface.h" #include "lldb/Core/ThreadSafeDenseMap.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/DeclVendor.h" #include "lldb/Symbol/Type.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/lldb-private.h" class CommandObjectObjC_ClassTable_Dump; namespace lldb_private { class UtilityFunction; class ObjCLanguageRuntime : public LanguageRuntime { public: enum class ObjCRuntimeVersions { eObjC_VersionUnknown = 0, eAppleObjC_V1 = 1, eAppleObjC_V2 = 2 }; typedef lldb::addr_t ObjCISA; class ClassDescriptor; typedef std::shared_ptr ClassDescriptorSP; // the information that we want to support retrieving from an ObjC class // this needs to be pure virtual since there are at least 2 different // implementations // of the runtime, and more might come class ClassDescriptor { public: ClassDescriptor() : m_is_kvo(eLazyBoolCalculate), m_is_cf(eLazyBoolCalculate), m_type_wp() {} virtual ~ClassDescriptor() = default; virtual ConstString GetClassName() = 0; virtual ClassDescriptorSP GetSuperclass() = 0; virtual ClassDescriptorSP GetMetaclass() const = 0; // virtual if any implementation has some other version-specific rules // but for the known v1/v2 this is all that needs to be done virtual bool IsKVO() { if (m_is_kvo == eLazyBoolCalculate) { const char *class_name = GetClassName().AsCString(); if (class_name && *class_name) m_is_kvo = (LazyBool)(strstr(class_name, "NSKVONotifying_") == class_name); } return (m_is_kvo == eLazyBoolYes); } // virtual if any implementation has some other version-specific rules // but for the known v1/v2 this is all that needs to be done virtual bool IsCFType() { if (m_is_cf == eLazyBoolCalculate) { const char *class_name = GetClassName().AsCString(); if (class_name && *class_name) m_is_cf = (LazyBool)(strcmp(class_name, "__NSCFType") == 0 || strcmp(class_name, "NSCFType") == 0); } return (m_is_cf == eLazyBoolYes); } virtual bool IsValid() = 0; virtual bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr, uint64_t *value_bits = nullptr, uint64_t *payload = nullptr) = 0; virtual uint64_t GetInstanceSize() = 0; // use to implement version-specific additional constraints on pointers virtual bool CheckPointer(lldb::addr_t value, uint32_t ptr_size) const { return true; } virtual ObjCISA GetISA() = 0; // This should return true iff the interface could be completed virtual bool Describe(std::function const &superclass_func, std::function const &instance_method_func, std::function const &class_method_func, std::function const &ivar_func) const { return false; } lldb::TypeSP GetType() { return m_type_wp.lock(); } void SetType(const lldb::TypeSP &type_sp) { m_type_wp = type_sp; } struct iVarDescriptor { ConstString m_name; CompilerType m_type; uint64_t m_size; int32_t m_offset; }; virtual size_t GetNumIVars() { return 0; } virtual iVarDescriptor GetIVarAtIndex(size_t idx) { return iVarDescriptor(); } protected: bool IsPointerValid(lldb::addr_t value, uint32_t ptr_size, bool allow_NULLs = false, bool allow_tagged = false, bool check_version_specific = false) const; private: LazyBool m_is_kvo; LazyBool m_is_cf; lldb::TypeWP m_type_wp; }; class EncodingToType { public: virtual ~EncodingToType(); virtual CompilerType RealizeType(ClangASTContext &ast_ctx, const char *name, bool for_expression); virtual CompilerType RealizeType(const char *name, bool for_expression); virtual CompilerType RealizeType(clang::ASTContext &ast_ctx, const char *name, bool for_expression) = 0; protected: std::unique_ptr m_scratch_ast_ctx_ap; }; class ObjCExceptionPrecondition : public Breakpoint::BreakpointPrecondition { public: ObjCExceptionPrecondition(); ~ObjCExceptionPrecondition() override = default; bool EvaluatePrecondition(StoppointCallbackContext &context) override; void GetDescription(Stream &stream, lldb::DescriptionLevel level) override; Error ConfigurePrecondition(Args &args) override; protected: void AddClassName(const char *class_name); private: std::unordered_set m_class_names; }; class TaggedPointerVendor { public: virtual ~TaggedPointerVendor() = default; virtual bool IsPossibleTaggedPointer(lldb::addr_t ptr) = 0; virtual ObjCLanguageRuntime::ClassDescriptorSP GetClassDescriptor(lldb::addr_t ptr) = 0; protected: TaggedPointerVendor() = default; private: DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendor); }; ~ObjCLanguageRuntime() override; virtual TaggedPointerVendor *GetTaggedPointerVendor() { return nullptr; } typedef std::shared_ptr EncodingToTypeSP; virtual EncodingToTypeSP GetEncodingToType(); virtual ClassDescriptorSP GetClassDescriptor(ValueObject &in_value); ClassDescriptorSP GetNonKVOClassDescriptor(ValueObject &in_value); virtual ClassDescriptorSP GetClassDescriptorFromClassName(const ConstString &class_name); virtual ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa); ClassDescriptorSP GetNonKVOClassDescriptor(ObjCISA isa); lldb::LanguageType GetLanguageType() const override { return lldb::eLanguageTypeObjC; } virtual bool IsModuleObjCLibrary(const lldb::ModuleSP &module_sp) = 0; virtual bool ReadObjCLibrary(const lldb::ModuleSP &module_sp) = 0; virtual bool HasReadObjCLibrary() = 0; virtual lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, bool stop_others) = 0; lldb::addr_t LookupInMethodCache(lldb::addr_t class_addr, lldb::addr_t sel); void AddToMethodCache(lldb::addr_t class_addr, lldb::addr_t sel, lldb::addr_t impl_addr); TypeAndOrName LookupInClassNameCache(lldb::addr_t class_addr); void AddToClassNameCache(lldb::addr_t class_addr, const char *name, lldb::TypeSP type_sp); void AddToClassNameCache(lldb::addr_t class_addr, const TypeAndOrName &class_or_type_name); lldb::TypeSP LookupInCompleteClassCache(ConstString &name); virtual UtilityFunction *CreateObjectChecker(const char *) = 0; virtual ObjCRuntimeVersions GetRuntimeVersion() const { return ObjCRuntimeVersions::eObjC_VersionUnknown; } bool IsValidISA(ObjCISA isa) { UpdateISAToDescriptorMap(); return m_isa_to_descriptor.count(isa) > 0; } virtual void UpdateISAToDescriptorMapIfNeeded() = 0; void UpdateISAToDescriptorMap() { if (m_process && m_process->GetStopID() != m_isa_to_descriptor_stop_id) { UpdateISAToDescriptorMapIfNeeded(); } } virtual ObjCISA GetISA(const ConstString &name); virtual ConstString GetActualTypeName(ObjCISA isa); virtual ObjCISA GetParentClass(ObjCISA isa); virtual DeclVendor *GetDeclVendor() { return nullptr; } // Finds the byte offset of the child_type ivar in parent_type. If it can't // find the // offset, returns LLDB_INVALID_IVAR_OFFSET. virtual size_t GetByteOffsetForIvar(CompilerType &parent_qual_type, const char *ivar_name); // Given the name of an Objective-C runtime symbol (e.g., ivar offset symbol), // try to determine from the runtime what the value of that symbol would be. // Useful when the underlying binary is stripped. virtual lldb::addr_t LookupRuntimeSymbol(const ConstString &name) { return LLDB_INVALID_ADDRESS; } bool HasNewLiteralsAndIndexing() { if (m_has_new_literals_and_indexing == eLazyBoolCalculate) { if (CalculateHasNewLiteralsAndIndexing()) m_has_new_literals_and_indexing = eLazyBoolYes; else m_has_new_literals_and_indexing = eLazyBoolNo; } return (m_has_new_literals_and_indexing == eLazyBoolYes); } virtual void SymbolsDidLoad(const ModuleList &module_list) { m_negative_complete_class_cache.clear(); } bool GetTypeBitSize(const CompilerType &compiler_type, uint64_t &size) override; protected: //------------------------------------------------------------------ // Classes that inherit from ObjCLanguageRuntime can see and modify these //------------------------------------------------------------------ ObjCLanguageRuntime(Process *process); virtual bool CalculateHasNewLiteralsAndIndexing() { return false; } bool ISAIsCached(ObjCISA isa) const { return m_isa_to_descriptor.find(isa) != m_isa_to_descriptor.end(); } bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp) { if (isa != 0) { m_isa_to_descriptor[isa] = descriptor_sp; return true; } return false; } bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp, const char *class_name); bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp, uint32_t class_name_hash) { if (isa != 0) { m_isa_to_descriptor[isa] = descriptor_sp; m_hash_to_isa_map.insert(std::make_pair(class_name_hash, isa)); return true; } return false; } private: // We keep a map of ->Implementation so we don't have to call // the resolver // function over and over. // FIXME: We need to watch for the loading of Protocols, and flush the cache // for any // class that we see so changed. struct ClassAndSel { ClassAndSel() { sel_addr = LLDB_INVALID_ADDRESS; class_addr = LLDB_INVALID_ADDRESS; } ClassAndSel(lldb::addr_t in_sel_addr, lldb::addr_t in_class_addr) : class_addr(in_class_addr), sel_addr(in_sel_addr) {} bool operator==(const ClassAndSel &rhs) { if (class_addr == rhs.class_addr && sel_addr == rhs.sel_addr) return true; else return false; } bool operator<(const ClassAndSel &rhs) const { if (class_addr < rhs.class_addr) return true; else if (class_addr > rhs.class_addr) return false; else { if (sel_addr < rhs.sel_addr) return true; else return false; } } lldb::addr_t class_addr; lldb::addr_t sel_addr; }; typedef std::map MsgImplMap; typedef std::map ISAToDescriptorMap; typedef std::multimap HashToISAMap; typedef ISAToDescriptorMap::iterator ISAToDescriptorIterator; typedef HashToISAMap::iterator HashToISAIterator; typedef ThreadSafeDenseMap TypeSizeCache; MsgImplMap m_impl_cache; LazyBool m_has_new_literals_and_indexing; ISAToDescriptorMap m_isa_to_descriptor; HashToISAMap m_hash_to_isa_map; TypeSizeCache m_type_size_cache; protected: uint32_t m_isa_to_descriptor_stop_id; typedef std::map CompleteClassMap; CompleteClassMap m_complete_class_cache; struct ConstStringSetHelpers { size_t operator()(const ConstString &arg) const // for hashing { return (size_t)arg.GetCString(); } bool operator()(const ConstString &arg1, const ConstString &arg2) const // for equality { return arg1.operator==(arg2); } }; typedef std::unordered_set CompleteClassSet; CompleteClassSet m_negative_complete_class_cache; ISAToDescriptorIterator GetDescriptorIterator(const ConstString &name); friend class ::CommandObjectObjC_ClassTable_Dump; std::pair GetDescriptorIteratorPair(bool update_if_needed = true); void ReadObjCLibraryIfNeeded(const ModuleList &module_list); DISALLOW_COPY_AND_ASSIGN(ObjCLanguageRuntime); }; } // namespace lldb_private #endif // liblldb_ObjCLanguageRuntime_h_