//===-- AppleObjCRuntimeV2.h ------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H #define LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H #include #include #include #include #include "AppleObjCRuntime.h" #include "lldb/lldb-private.h" #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" #include "llvm/ADT/BitVector.h" class RemoteNXMapTable; namespace lldb_private { class AppleObjCRuntimeV2 : public AppleObjCRuntime { public: ~AppleObjCRuntimeV2() override = default; static void Initialize(); static void Terminate(); static lldb_private::LanguageRuntime * CreateInstance(Process *process, lldb::LanguageType language); static llvm::StringRef GetPluginNameStatic() { return "apple-objc-v2"; } LanguageRuntime *GetPreferredLanguageRuntime(ValueObject &in_value) override; static char ID; bool isA(const void *ClassID) const override { return ClassID == &ID || AppleObjCRuntime::isA(ClassID); } static bool classof(const LanguageRuntime *runtime) { return runtime->isA(&ID); } bool GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, Address &address, Value::ValueType &value_type) override; llvm::Expected> CreateObjectChecker(std::string name, ExecutionContext &exe_ctx) override; llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } ObjCRuntimeVersions GetRuntimeVersion() const override { return ObjCRuntimeVersions::eAppleObjC_V2; } size_t GetByteOffsetForIvar(CompilerType &parent_ast_type, const char *ivar_name) override; void UpdateISAToDescriptorMapIfNeeded() override; ClassDescriptorSP GetClassDescriptor(ValueObject &valobj) override; ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa) override; DeclVendor *GetDeclVendor() override; lldb::addr_t LookupRuntimeSymbol(ConstString name) override; EncodingToTypeSP GetEncodingToType() override; bool IsTaggedPointer(lldb::addr_t ptr) override; TaggedPointerVendor *GetTaggedPointerVendor() override { return m_tagged_pointer_vendor_up.get(); } lldb::addr_t GetTaggedPointerObfuscator(); /// Returns the base address for relative method list selector strings. lldb::addr_t GetRelativeSelectorBaseAddr() { return m_relative_selector_base; } void SetRelativeSelectorBaseAddr(lldb::addr_t relative_selector_base) { m_relative_selector_base = relative_selector_base; } void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true, lldb::addr_t &cf_false) override; void ModulesDidLoad(const ModuleList &module_list) override; bool IsSharedCacheImageLoaded(uint16_t image_index); std::optional GetSharedCacheImageHeaderVersion(); protected: lldb::BreakpointResolverSP CreateExceptionResolver(const lldb::BreakpointSP &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 = 0; uint32_t m_num_buckets = 0; lldb::addr_t m_buckets_ptr = 0; }; class NonPointerISACache { public: static NonPointerISACache * CreateInstance(AppleObjCRuntimeV2 &runtime, const lldb::ModuleSP &objc_module_sp); ObjCLanguageRuntime::ClassDescriptorSP GetClassDescriptor(ObjCISA isa); private: NonPointerISACache(AppleObjCRuntimeV2 &runtime, const lldb::ModuleSP &objc_module_sp, uint64_t objc_debug_isa_class_mask, uint64_t objc_debug_isa_magic_mask, uint64_t objc_debug_isa_magic_value, uint64_t objc_debug_indexed_isa_magic_mask, uint64_t objc_debug_indexed_isa_magic_value, uint64_t objc_debug_indexed_isa_index_mask, uint64_t objc_debug_indexed_isa_index_shift, lldb::addr_t objc_indexed_classes); bool EvaluateNonPointerISA(ObjCISA isa, ObjCISA &ret_isa); AppleObjCRuntimeV2 &m_runtime; std::map m_cache; lldb::ModuleWP m_objc_module_wp; uint64_t m_objc_debug_isa_class_mask; uint64_t m_objc_debug_isa_magic_mask; uint64_t m_objc_debug_isa_magic_value; uint64_t m_objc_debug_indexed_isa_magic_mask; uint64_t m_objc_debug_indexed_isa_magic_value; uint64_t m_objc_debug_indexed_isa_index_mask; uint64_t m_objc_debug_indexed_isa_index_shift; lldb::addr_t m_objc_indexed_classes; std::vector m_indexed_isa_cache; friend class AppleObjCRuntimeV2; NonPointerISACache(const NonPointerISACache &) = delete; const NonPointerISACache &operator=(const NonPointerISACache &) = delete; }; 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: TaggedPointerVendorV2(const TaggedPointerVendorV2 &) = delete; const TaggedPointerVendorV2 & operator=(const TaggedPointerVendorV2 &) = delete; }; 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 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; TaggedPointerVendorRuntimeAssisted( const TaggedPointerVendorRuntimeAssisted &) = delete; const TaggedPointerVendorRuntimeAssisted & operator=(const TaggedPointerVendorRuntimeAssisted &) = delete; }; class TaggedPointerVendorExtended : public TaggedPointerVendorRuntimeAssisted { public: ObjCLanguageRuntime::ClassDescriptorSP GetClassDescriptor(lldb::addr_t ptr) override; protected: TaggedPointerVendorExtended( AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask, uint64_t objc_debug_taggedpointer_ext_mask, uint32_t objc_debug_taggedpointer_slot_shift, uint32_t objc_debug_taggedpointer_ext_slot_shift, uint32_t objc_debug_taggedpointer_slot_mask, uint32_t objc_debug_taggedpointer_ext_slot_mask, uint32_t objc_debug_taggedpointer_payload_lshift, uint32_t objc_debug_taggedpointer_payload_rshift, uint32_t objc_debug_taggedpointer_ext_payload_lshift, uint32_t objc_debug_taggedpointer_ext_payload_rshift, lldb::addr_t objc_debug_taggedpointer_classes, lldb::addr_t objc_debug_taggedpointer_ext_classes); bool IsPossibleExtendedTaggedPointer(lldb::addr_t ptr); typedef std::map Cache; typedef Cache::iterator CacheIterator; Cache m_ext_cache; uint64_t m_objc_debug_taggedpointer_ext_mask; uint32_t m_objc_debug_taggedpointer_ext_slot_shift; uint32_t m_objc_debug_taggedpointer_ext_slot_mask; uint32_t m_objc_debug_taggedpointer_ext_payload_lshift; uint32_t m_objc_debug_taggedpointer_ext_payload_rshift; lldb::addr_t m_objc_debug_taggedpointer_ext_classes; friend class AppleObjCRuntimeV2::TaggedPointerVendorV2; TaggedPointerVendorExtended(const TaggedPointerVendorExtended &) = delete; const TaggedPointerVendorExtended & operator=(const TaggedPointerVendorExtended &) = delete; }; 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; TaggedPointerVendorLegacy(const TaggedPointerVendorLegacy &) = delete; const TaggedPointerVendorLegacy & operator=(const TaggedPointerVendorLegacy &) = delete; }; struct DescriptorMapUpdateResult { bool m_update_ran; bool m_retry_update; uint32_t m_num_found; DescriptorMapUpdateResult(bool ran, bool retry, uint32_t found) { m_update_ran = ran; m_retry_update = retry; m_num_found = found; } static DescriptorMapUpdateResult Fail() { return {false, false, 0}; } static DescriptorMapUpdateResult Success(uint32_t found) { return {true, false, found}; } static DescriptorMapUpdateResult Retry() { return {false, true, 0}; } }; /// Abstraction to read the Objective-C class info. class ClassInfoExtractor { public: ClassInfoExtractor(AppleObjCRuntimeV2 &runtime) : m_runtime(runtime) {} std::mutex &GetMutex() { return m_mutex; } protected: /// The lifetime of this object is tied to that of the runtime. AppleObjCRuntimeV2 &m_runtime; std::mutex m_mutex; }; /// We can read the class info from the Objective-C runtime using /// gdb_objc_realized_classes, objc_copyRealizedClassList or /// objc_getRealizedClassList_trylock. The RealizedClassList variants are /// preferred because they include lazily named classes, but they are not /// always available or safe to call. /// /// We potentially need more than one helper for the same process, because we /// may need to use gdb_objc_realized_classes until dyld is initialized and /// then switch over to objc_copyRealizedClassList or /// objc_getRealizedClassList_trylock for lazily named classes. class DynamicClassInfoExtractor : public ClassInfoExtractor { public: DynamicClassInfoExtractor(AppleObjCRuntimeV2 &runtime) : ClassInfoExtractor(runtime) {} DescriptorMapUpdateResult UpdateISAToDescriptorMap(RemoteNXMapTable &hash_table); private: enum Helper { gdb_objc_realized_classes, objc_copyRealizedClassList, objc_getRealizedClassList_trylock }; /// Compute which helper to use. If dyld is not yet fully initialized we /// must use gdb_objc_realized_classes. Otherwise, we prefer /// objc_getRealizedClassList_trylock and objc_copyRealizedClassList /// respectively, depending on availability. Helper ComputeHelper(ExecutionContext &exe_ctx) const; UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx, Helper helper); lldb::addr_t &GetClassInfoArgs(Helper helper); std::unique_ptr GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx, Helper helper, std::string code, std::string name); struct UtilityFunctionHelper { std::unique_ptr utility_function; lldb::addr_t args = LLDB_INVALID_ADDRESS; }; UtilityFunctionHelper m_gdb_objc_realized_classes_helper; UtilityFunctionHelper m_objc_copyRealizedClassList_helper; UtilityFunctionHelper m_objc_getRealizedClassList_trylock_helper; }; /// Abstraction to read the Objective-C class info from the shared cache. class SharedCacheClassInfoExtractor : public ClassInfoExtractor { public: SharedCacheClassInfoExtractor(AppleObjCRuntimeV2 &runtime) : ClassInfoExtractor(runtime) {} DescriptorMapUpdateResult UpdateISAToDescriptorMap(); private: UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx); std::unique_ptr GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx); std::unique_ptr m_utility_function; lldb::addr_t m_args = LLDB_INVALID_ADDRESS; }; class SharedCacheImageHeaders { public: static std::unique_ptr CreateSharedCacheImageHeaders(AppleObjCRuntimeV2 &runtime); void SetNeedsUpdate() { m_needs_update = true; } bool IsImageLoaded(uint16_t image_index); uint64_t GetVersion(); private: SharedCacheImageHeaders(AppleObjCRuntimeV2 &runtime, lldb::addr_t headerInfoRWs_ptr, uint32_t count, uint32_t entsize) : m_runtime(runtime), m_headerInfoRWs_ptr(headerInfoRWs_ptr), m_loaded_images(count, false), m_version(0), m_count(count), m_entsize(entsize), m_needs_update(true) {} llvm::Error UpdateIfNeeded(); AppleObjCRuntimeV2 &m_runtime; lldb::addr_t m_headerInfoRWs_ptr; llvm::BitVector m_loaded_images; uint64_t m_version; uint32_t m_count; uint32_t m_entsize; bool m_needs_update; }; AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp); ObjCISA GetPointerISA(ObjCISA isa); lldb::addr_t GetISAHashTablePointer(); /// Update the generation count of realized classes. This is not an exact /// count but rather a value that is incremented when new classes are realized /// or destroyed. Unlike the count in gdb_objc_realized_classes, it will /// change when lazily named classes get realized. bool RealizedClassGenerationCountChanged(); uint32_t ParseClassInfoArray(const lldb_private::DataExtractor &data, uint32_t num_class_infos); enum class SharedCacheWarningReason { eExpressionUnableToRun, eExpressionExecutionFailure, eNotEnoughClassesRead }; void WarnIfNoClassesCached(SharedCacheWarningReason reason); void WarnIfNoExpandedSharedCache(); lldb::addr_t GetSharedCacheReadOnlyAddress(); lldb::addr_t GetSharedCacheBaseAddress(); bool GetCFBooleanValuesIfNeeded(); bool HasSymbol(ConstString Name); NonPointerISACache *GetNonPointerIsaCache() { if (!m_non_pointer_isa_cache_up) m_non_pointer_isa_cache_up.reset( NonPointerISACache::CreateInstance(*this, m_objc_module_sp)); return m_non_pointer_isa_cache_up.get(); } friend class ClassDescriptorV2; lldb::ModuleSP m_objc_module_sp; DynamicClassInfoExtractor m_dynamic_class_info_extractor; SharedCacheClassInfoExtractor m_shared_cache_class_info_extractor; std::unique_ptr m_decl_vendor_up; lldb::addr_t m_tagged_pointer_obfuscator; lldb::addr_t m_isa_hash_table_ptr; lldb::addr_t m_relative_selector_base; HashTableSignature m_hash_signature; bool m_has_object_getClass; bool m_has_objc_copyRealizedClassList; bool m_has_objc_getRealizedClassList_trylock; bool m_loaded_objc_opt; std::unique_ptr m_non_pointer_isa_cache_up; std::unique_ptr m_tagged_pointer_vendor_up; EncodingToTypeSP m_encoding_to_type_sp; std::once_flag m_no_classes_cached_warning; std::once_flag m_no_expanded_cache_warning; std::optional> m_CFBoolean_values; uint64_t m_realized_class_generation_count; std::unique_ptr m_shared_cache_image_headers_up; }; } // namespace lldb_private #endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H