482 lines
16 KiB
C++
482 lines
16 KiB
C++
//===-- 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 <map>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <optional>
|
|
|
|
#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<std::unique_ptr<UtilityFunction>>
|
|
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<uint64_t> 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<ObjCISA, ObjCLanguageRuntime::ClassDescriptorSP> 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<lldb::addr_t> 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<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;
|
|
|
|
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<uint8_t, ObjCLanguageRuntime::ClassDescriptorSP> 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<UtilityFunction>
|
|
GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx, Helper helper,
|
|
std::string code, std::string name);
|
|
|
|
struct UtilityFunctionHelper {
|
|
std::unique_ptr<UtilityFunction> 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<UtilityFunction>
|
|
GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx);
|
|
|
|
std::unique_ptr<UtilityFunction> m_utility_function;
|
|
lldb::addr_t m_args = LLDB_INVALID_ADDRESS;
|
|
};
|
|
|
|
class SharedCacheImageHeaders {
|
|
public:
|
|
static std::unique_ptr<SharedCacheImageHeaders>
|
|
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<DeclVendor> 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<NonPointerISACache> m_non_pointer_isa_cache_up;
|
|
std::unique_ptr<TaggedPointerVendor> 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<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values;
|
|
uint64_t m_realized_class_generation_count;
|
|
std::unique_ptr<SharedCacheImageHeaders> m_shared_cache_image_headers_up;
|
|
};
|
|
|
|
} // namespace lldb_private
|
|
|
|
#endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_APPLEOBJCRUNTIME_APPLEOBJCRUNTIMEV2_H
|