Path: blob/main/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
39644 views
//===-- ObjCLanguageRuntime.cpp -------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7#include "clang/AST/Type.h"89#include "ObjCLanguageRuntime.h"1011#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"12#include "lldb/Core/Module.h"13#include "lldb/Core/PluginManager.h"14#include "lldb/Core/ValueObject.h"15#include "lldb/Symbol/SymbolContext.h"16#include "lldb/Symbol/SymbolFile.h"17#include "lldb/Symbol/Type.h"18#include "lldb/Symbol/TypeList.h"19#include "lldb/Symbol/Variable.h"20#include "lldb/Target/ABI.h"21#include "lldb/Target/Target.h"22#include "lldb/Utility/LLDBLog.h"23#include "lldb/Utility/Log.h"24#include "lldb/Utility/Timer.h"2526#include "llvm/ADT/StringRef.h"27#include "llvm/Support/DJB.h"28#include <optional>2930using namespace lldb;31using namespace lldb_private;3233char ObjCLanguageRuntime::ID = 0;3435// Destructor36ObjCLanguageRuntime::~ObjCLanguageRuntime() = default;3738ObjCLanguageRuntime::ObjCLanguageRuntime(Process *process)39: LanguageRuntime(process), m_impl_cache(), m_impl_str_cache(),40m_has_new_literals_and_indexing(eLazyBoolCalculate),41m_isa_to_descriptor(), m_hash_to_isa_map(), m_type_size_cache(),42m_isa_to_descriptor_stop_id(UINT32_MAX), m_complete_class_cache(),43m_negative_complete_class_cache() {}4445bool ObjCLanguageRuntime::IsAllowedRuntimeValue(ConstString name) {46static ConstString g_self = ConstString("self");47static ConstString g_cmd = ConstString("_cmd");48return name == g_self || name == g_cmd;49}5051bool ObjCLanguageRuntime::AddClass(ObjCISA isa,52const ClassDescriptorSP &descriptor_sp,53const char *class_name) {54if (isa != 0) {55m_isa_to_descriptor[isa] = descriptor_sp;56// class_name is assumed to be valid57m_hash_to_isa_map.insert(std::make_pair(llvm::djbHash(class_name), isa));58return true;59}60return false;61}6263void ObjCLanguageRuntime::AddToMethodCache(lldb::addr_t class_addr,64lldb::addr_t selector,65lldb::addr_t impl_addr) {66Log *log = GetLog(LLDBLog::Step);67if (log) {68LLDB_LOGF(log,69"Caching: class 0x%" PRIx64 " selector 0x%" PRIx6470" implementation 0x%" PRIx64 ".",71class_addr, selector, impl_addr);72}73m_impl_cache.insert(std::pair<ClassAndSel, lldb::addr_t>(74ClassAndSel(class_addr, selector), impl_addr));75}7677void ObjCLanguageRuntime::AddToMethodCache(lldb::addr_t class_addr,78llvm::StringRef sel_str,79lldb::addr_t impl_addr) {80Log *log = GetLog(LLDBLog::Step);8182LLDB_LOG(log, "Caching: class {0} selector {1} implementation {2}.",83class_addr, sel_str, impl_addr);8485m_impl_str_cache.insert(std::pair<ClassAndSelStr, lldb::addr_t>(86ClassAndSelStr(class_addr, sel_str), impl_addr));87}8889lldb::addr_t ObjCLanguageRuntime::LookupInMethodCache(lldb::addr_t class_addr,90lldb::addr_t selector) {91MsgImplMap::iterator pos, end = m_impl_cache.end();92pos = m_impl_cache.find(ClassAndSel(class_addr, selector));93if (pos != end)94return (*pos).second;95return LLDB_INVALID_ADDRESS;96}9798lldb::addr_t ObjCLanguageRuntime::LookupInMethodCache(lldb::addr_t class_addr,99llvm::StringRef sel_str) {100MsgImplStrMap::iterator pos, end = m_impl_str_cache.end();101pos = m_impl_str_cache.find(ClassAndSelStr(class_addr, sel_str));102if (pos != end)103return (*pos).second;104return LLDB_INVALID_ADDRESS;105}106107lldb::TypeSP108ObjCLanguageRuntime::LookupInCompleteClassCache(ConstString &name) {109CompleteClassMap::iterator complete_class_iter =110m_complete_class_cache.find(name);111112if (complete_class_iter != m_complete_class_cache.end()) {113// Check the weak pointer to make sure the type hasn't been unloaded114TypeSP complete_type_sp(complete_class_iter->second.lock());115116if (complete_type_sp)117return complete_type_sp;118else119m_complete_class_cache.erase(name);120}121122if (m_negative_complete_class_cache.count(name) > 0)123return TypeSP();124125const ModuleList &modules = m_process->GetTarget().GetImages();126127SymbolContextList sc_list;128modules.FindSymbolsWithNameAndType(name, eSymbolTypeObjCClass, sc_list);129const size_t matching_symbols = sc_list.GetSize();130131if (matching_symbols) {132SymbolContext sc;133134sc_list.GetContextAtIndex(0, sc);135136ModuleSP module_sp(sc.module_sp);137138if (!module_sp)139return TypeSP();140141TypeQuery query(name.GetStringRef(), TypeQueryOptions::e_exact_match);142TypeResults results;143module_sp->FindTypes(query, results);144for (const TypeSP &type_sp : results.GetTypeMap().Types()) {145if (TypeSystemClang::IsObjCObjectOrInterfaceType(146type_sp->GetForwardCompilerType())) {147if (TypePayloadClang(type_sp->GetPayload()).IsCompleteObjCClass()) {148m_complete_class_cache[name] = type_sp;149return type_sp;150}151}152}153}154m_negative_complete_class_cache.insert(name);155return TypeSP();156}157158size_t ObjCLanguageRuntime::GetByteOffsetForIvar(CompilerType &parent_qual_type,159const char *ivar_name) {160return LLDB_INVALID_IVAR_OFFSET;161}162163bool ObjCLanguageRuntime::ClassDescriptor::IsPointerValid(164lldb::addr_t value, uint32_t ptr_size, bool allow_NULLs, bool allow_tagged,165bool check_version_specific) const {166if (!value)167return allow_NULLs;168if ((value % 2) == 1 && allow_tagged)169return true;170if ((value % ptr_size) == 0)171return (check_version_specific ? CheckPointer(value, ptr_size) : true);172else173return false;174}175176ObjCLanguageRuntime::ObjCISA177ObjCLanguageRuntime::GetISA(ConstString name) {178ISAToDescriptorIterator pos = GetDescriptorIterator(name);179if (pos != m_isa_to_descriptor.end())180return pos->first;181return 0;182}183184ObjCLanguageRuntime::ISAToDescriptorIterator185ObjCLanguageRuntime::GetDescriptorIterator(ConstString name) {186ISAToDescriptorIterator end = m_isa_to_descriptor.end();187188if (name) {189UpdateISAToDescriptorMap();190if (m_hash_to_isa_map.empty()) {191// No name hashes were provided, we need to just linearly power through192// the names and find a match193for (ISAToDescriptorIterator pos = m_isa_to_descriptor.begin();194pos != end; ++pos) {195if (pos->second->GetClassName() == name)196return pos;197}198} else {199// Name hashes were provided, so use them to efficiently lookup name to200// isa/descriptor201const uint32_t name_hash = llvm::djbHash(name.GetStringRef());202std::pair<HashToISAIterator, HashToISAIterator> range =203m_hash_to_isa_map.equal_range(name_hash);204for (HashToISAIterator range_pos = range.first; range_pos != range.second;205++range_pos) {206ISAToDescriptorIterator pos =207m_isa_to_descriptor.find(range_pos->second);208if (pos != m_isa_to_descriptor.end()) {209if (pos->second->GetClassName() == name)210return pos;211}212}213}214}215return end;216}217218std::pair<ObjCLanguageRuntime::ISAToDescriptorIterator,219ObjCLanguageRuntime::ISAToDescriptorIterator>220ObjCLanguageRuntime::GetDescriptorIteratorPair(bool update_if_needed) {221if (update_if_needed)222UpdateISAToDescriptorMapIfNeeded();223224return std::pair<ObjCLanguageRuntime::ISAToDescriptorIterator,225ObjCLanguageRuntime::ISAToDescriptorIterator>(226m_isa_to_descriptor.begin(), m_isa_to_descriptor.end());227}228229void ObjCLanguageRuntime::ReadObjCLibraryIfNeeded(230const ModuleList &module_list) {231if (!HasReadObjCLibrary()) {232std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());233234size_t num_modules = module_list.GetSize();235for (size_t i = 0; i < num_modules; i++) {236auto mod = module_list.GetModuleAtIndex(i);237if (IsModuleObjCLibrary(mod)) {238ReadObjCLibrary(mod);239break;240}241}242}243}244245ObjCLanguageRuntime::ObjCISA246ObjCLanguageRuntime::GetParentClass(ObjCLanguageRuntime::ObjCISA isa) {247ClassDescriptorSP objc_class_sp(GetClassDescriptorFromISA(isa));248if (objc_class_sp) {249ClassDescriptorSP objc_super_class_sp(objc_class_sp->GetSuperclass());250if (objc_super_class_sp)251return objc_super_class_sp->GetISA();252}253return 0;254}255256ObjCLanguageRuntime::ClassDescriptorSP257ObjCLanguageRuntime::GetClassDescriptorFromClassName(258ConstString class_name) {259ISAToDescriptorIterator pos = GetDescriptorIterator(class_name);260if (pos != m_isa_to_descriptor.end())261return pos->second;262return ClassDescriptorSP();263}264265ObjCLanguageRuntime::ClassDescriptorSP266ObjCLanguageRuntime::GetClassDescriptor(ValueObject &valobj) {267ClassDescriptorSP objc_class_sp;268// if we get an invalid VO (which might still happen when playing around with269// pointers returned by the expression parser, don't consider this a valid270// ObjC object)271if (valobj.GetCompilerType().IsValid()) {272addr_t isa_pointer = valobj.GetPointerValue();273if (isa_pointer != LLDB_INVALID_ADDRESS) {274ExecutionContext exe_ctx(valobj.GetExecutionContextRef());275276Process *process = exe_ctx.GetProcessPtr();277if (process) {278Status error;279ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error);280if (isa != LLDB_INVALID_ADDRESS)281objc_class_sp = GetClassDescriptorFromISA(isa);282}283}284}285return objc_class_sp;286}287288ObjCLanguageRuntime::ClassDescriptorSP289ObjCLanguageRuntime::GetNonKVOClassDescriptor(ValueObject &valobj) {290ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp(291GetClassDescriptor(valobj));292if (objc_class_sp) {293if (!objc_class_sp->IsKVO())294return objc_class_sp;295296ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());297if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())298return non_kvo_objc_class_sp;299}300return ClassDescriptorSP();301}302303ObjCLanguageRuntime::ClassDescriptorSP304ObjCLanguageRuntime::GetClassDescriptorFromISA(ObjCISA isa) {305if (isa) {306UpdateISAToDescriptorMap();307308ObjCLanguageRuntime::ISAToDescriptorIterator pos =309m_isa_to_descriptor.find(isa);310if (pos != m_isa_to_descriptor.end())311return pos->second;312313if (ABISP abi_sp = m_process->GetABI()) {314pos = m_isa_to_descriptor.find(abi_sp->FixCodeAddress(isa));315if (pos != m_isa_to_descriptor.end())316return pos->second;317}318}319return ClassDescriptorSP();320}321322ObjCLanguageRuntime::ClassDescriptorSP323ObjCLanguageRuntime::GetNonKVOClassDescriptor(ObjCISA isa) {324if (isa) {325ClassDescriptorSP objc_class_sp = GetClassDescriptorFromISA(isa);326if (objc_class_sp && objc_class_sp->IsValid()) {327if (!objc_class_sp->IsKVO())328return objc_class_sp;329330ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());331if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())332return non_kvo_objc_class_sp;333}334}335return ClassDescriptorSP();336}337338CompilerType339ObjCLanguageRuntime::EncodingToType::RealizeType(const char *name,340bool for_expression) {341if (m_scratch_ast_ctx_sp)342return RealizeType(*m_scratch_ast_ctx_sp, name, for_expression);343return CompilerType();344}345346ObjCLanguageRuntime::EncodingToType::~EncodingToType() = default;347348ObjCLanguageRuntime::EncodingToTypeSP ObjCLanguageRuntime::GetEncodingToType() {349return nullptr;350}351352std::optional<uint64_t>353ObjCLanguageRuntime::GetTypeBitSize(const CompilerType &compiler_type) {354void *opaque_ptr = compiler_type.GetOpaqueQualType();355uint64_t cached_size = m_type_size_cache.Lookup(opaque_ptr);356if (cached_size > 0)357return cached_size;358359ClassDescriptorSP class_descriptor_sp =360GetClassDescriptorFromClassName(compiler_type.GetTypeName());361if (!class_descriptor_sp)362return {};363364int32_t max_offset = INT32_MIN;365uint64_t sizeof_max = 0;366bool found = false;367368for (size_t idx = 0; idx < class_descriptor_sp->GetNumIVars(); idx++) {369const auto &ivar = class_descriptor_sp->GetIVarAtIndex(idx);370int32_t cur_offset = ivar.m_offset;371if (cur_offset > max_offset) {372max_offset = cur_offset;373sizeof_max = ivar.m_size;374found = true;375}376}377378uint64_t size = 8 * (max_offset + sizeof_max);379if (found && size > 0) {380m_type_size_cache.Insert(opaque_ptr, size);381return size;382}383384return {};385}386387lldb::BreakpointPreconditionSP388ObjCLanguageRuntime::GetBreakpointExceptionPrecondition(LanguageType language,389bool throw_bp) {390if (language != eLanguageTypeObjC)391return lldb::BreakpointPreconditionSP();392if (!throw_bp)393return lldb::BreakpointPreconditionSP();394BreakpointPreconditionSP precondition_sp(395new ObjCLanguageRuntime::ObjCExceptionPrecondition());396return precondition_sp;397}398399// Exception breakpoint Precondition class for ObjC:400void ObjCLanguageRuntime::ObjCExceptionPrecondition::AddClassName(401const char *class_name) {402m_class_names.insert(class_name);403}404405ObjCLanguageRuntime::ObjCExceptionPrecondition::ObjCExceptionPrecondition() =406default;407408bool ObjCLanguageRuntime::ObjCExceptionPrecondition::EvaluatePrecondition(409StoppointCallbackContext &context) {410return true;411}412413void ObjCLanguageRuntime::ObjCExceptionPrecondition::GetDescription(414Stream &stream, lldb::DescriptionLevel level) {}415416Status ObjCLanguageRuntime::ObjCExceptionPrecondition::ConfigurePrecondition(417Args &args) {418Status error;419if (args.GetArgumentCount() > 0)420error.SetErrorString(421"The ObjC Exception breakpoint doesn't support extra options.");422return error;423}424425std::optional<CompilerType>426ObjCLanguageRuntime::GetRuntimeType(CompilerType base_type) {427CompilerType class_type;428bool is_pointer_type = false;429430if (TypeSystemClang::IsObjCObjectPointerType(base_type, &class_type))431is_pointer_type = true;432else if (TypeSystemClang::IsObjCObjectOrInterfaceType(base_type))433class_type = base_type;434else435return std::nullopt;436437if (!class_type)438return std::nullopt;439440ConstString class_name(class_type.GetTypeName());441if (!class_name)442return std::nullopt;443444TypeSP complete_objc_class_type_sp = LookupInCompleteClassCache(class_name);445if (!complete_objc_class_type_sp)446return std::nullopt;447448CompilerType complete_class(449complete_objc_class_type_sp->GetFullCompilerType());450if (complete_class.GetCompleteType()) {451if (is_pointer_type)452return complete_class.GetPointerType();453else454return complete_class;455}456457return std::nullopt;458}459460461