Path: blob/main/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp
39690 views
//===-- AppleObjCRuntimeV1.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//===----------------------------------------------------------------------===//78#include "AppleObjCRuntimeV1.h"9#include "AppleObjCDeclVendor.h"10#include "AppleObjCTrampolineHandler.h"1112#include "clang/AST/Type.h"1314#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"15#include "lldb/Breakpoint/BreakpointLocation.h"16#include "lldb/Core/Module.h"17#include "lldb/Core/PluginManager.h"18#include "lldb/Expression/FunctionCaller.h"19#include "lldb/Expression/UtilityFunction.h"20#include "lldb/Symbol/Symbol.h"21#include "lldb/Target/ExecutionContext.h"22#include "lldb/Target/Process.h"23#include "lldb/Target/RegisterContext.h"24#include "lldb/Target/Target.h"25#include "lldb/Target/Thread.h"26#include "lldb/Utility/ConstString.h"27#include "lldb/Utility/LLDBLog.h"28#include "lldb/Utility/Log.h"29#include "lldb/Utility/Scalar.h"30#include "lldb/Utility/Status.h"31#include "lldb/Utility/StreamString.h"3233#include <memory>34#include <vector>3536using namespace lldb;37using namespace lldb_private;3839char AppleObjCRuntimeV1::ID = 0;4041AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process)42: AppleObjCRuntime(process), m_hash_signature(),43m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS) {}4445// for V1 runtime we just try to return a class name as that is the minimum46// level of support required for the data formatters to work47bool AppleObjCRuntimeV1::GetDynamicTypeAndAddress(48ValueObject &in_value, lldb::DynamicValueType use_dynamic,49TypeAndOrName &class_type_or_name, Address &address,50Value::ValueType &value_type) {51class_type_or_name.Clear();52value_type = Value::ValueType::Scalar;53if (CouldHaveDynamicValue(in_value)) {54auto class_descriptor(GetClassDescriptor(in_value));55if (class_descriptor && class_descriptor->IsValid() &&56class_descriptor->GetClassName()) {57const addr_t object_ptr = in_value.GetPointerValue();58address.SetRawAddress(object_ptr);59class_type_or_name.SetName(class_descriptor->GetClassName());60}61}62return !class_type_or_name.IsEmpty();63}6465// Static Functions66lldb_private::LanguageRuntime *67AppleObjCRuntimeV1::CreateInstance(Process *process,68lldb::LanguageType language) {69// FIXME: This should be a MacOS or iOS process, and we need to look for the70// OBJC section to make71// sure we aren't using the V1 runtime.72if (language == eLanguageTypeObjC) {73ModuleSP objc_module_sp;7475if (AppleObjCRuntime::GetObjCVersion(process, objc_module_sp) ==76ObjCRuntimeVersions::eAppleObjC_V1)77return new AppleObjCRuntimeV1(process);78else79return nullptr;80} else81return nullptr;82}8384void AppleObjCRuntimeV1::Initialize() {85PluginManager::RegisterPlugin(86GetPluginNameStatic(), "Apple Objective-C Language Runtime - Version 1",87CreateInstance,88/*command_callback = */ nullptr, GetBreakpointExceptionPrecondition);89}9091void AppleObjCRuntimeV1::Terminate() {92PluginManager::UnregisterPlugin(CreateInstance);93}9495BreakpointResolverSP96AppleObjCRuntimeV1::CreateExceptionResolver(const BreakpointSP &bkpt,97bool catch_bp, bool throw_bp) {98BreakpointResolverSP resolver_sp;99100if (throw_bp)101resolver_sp = std::make_shared<BreakpointResolverName>(102bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(),103eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0,104eLazyBoolNo);105// FIXME: don't do catch yet.106return resolver_sp;107}108109struct BufStruct {110char contents[2048];111};112113llvm::Expected<std::unique_ptr<UtilityFunction>>114AppleObjCRuntimeV1::CreateObjectChecker(std::string name,115ExecutionContext &exe_ctx) {116std::unique_ptr<BufStruct> buf(new BufStruct);117118int strformatsize =119snprintf(&buf->contents[0], sizeof(buf->contents),120"struct __objc_class "121" \n"122"{ "123" \n"124" struct __objc_class *isa; "125" \n"126" struct __objc_class *super_class; "127" \n"128" const char *name; "129" \n"130" // rest of struct elided because unused "131" \n"132"}; "133" \n"134" "135" \n"136"struct __objc_object "137" \n"138"{ "139" \n"140" struct __objc_class *isa; "141" \n"142"}; "143" \n"144" "145" \n"146"extern \"C\" void "147" \n"148"%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) "149" \n"150"{ "151" \n"152" struct __objc_object *obj = (struct "153"__objc_object*)$__lldb_arg_obj; \n"154" if ($__lldb_arg_obj == (void *)0) "155" \n"156" return; // nil is ok "157" (int)strlen(obj->isa->name); "158" \n"159"} "160" \n",161name.c_str());162assert(strformatsize < (int)sizeof(buf->contents));163UNUSED_IF_ASSERT_DISABLED(strformatsize);164165return GetTargetRef().CreateUtilityFunction(buf->contents, std::move(name),166eLanguageTypeC, exe_ctx);167}168169AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(170ValueObject &isa_pointer) {171Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP());172}173174AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(175ObjCISA isa, lldb::ProcessSP process_sp) {176Initialize(isa, process_sp);177}178179void AppleObjCRuntimeV1::ClassDescriptorV1::Initialize(180ObjCISA isa, lldb::ProcessSP process_sp) {181if (!isa || !process_sp) {182m_valid = false;183return;184}185186m_valid = true;187188Status error;189190m_isa = process_sp->ReadPointerFromMemory(isa, error);191192if (error.Fail()) {193m_valid = false;194return;195}196197uint32_t ptr_size = process_sp->GetAddressByteSize();198199if (!IsPointerValid(m_isa, ptr_size)) {200m_valid = false;201return;202}203204m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size, error);205206if (error.Fail()) {207m_valid = false;208return;209}210211if (!IsPointerValid(m_parent_isa, ptr_size, true)) {212m_valid = false;213return;214}215216lldb::addr_t name_ptr =217process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size, error);218219if (error.Fail()) {220m_valid = false;221return;222}223224lldb::WritableDataBufferSP buffer_sp(new DataBufferHeap(1024, 0));225226size_t count = process_sp->ReadCStringFromMemory(227name_ptr, (char *)buffer_sp->GetBytes(), 1024, error);228229if (error.Fail()) {230m_valid = false;231return;232}233234if (count)235m_name = ConstString(reinterpret_cast<const char *>(buffer_sp->GetBytes()));236else237m_name = ConstString();238239m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(240m_isa + 5 * ptr_size, ptr_size, 0, error);241242if (error.Fail()) {243m_valid = false;244return;245}246247m_process_wp = lldb::ProcessWP(process_sp);248}249250AppleObjCRuntime::ClassDescriptorSP251AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() {252if (!m_valid)253return AppleObjCRuntime::ClassDescriptorSP();254ProcessSP process_sp = m_process_wp.lock();255if (!process_sp)256return AppleObjCRuntime::ClassDescriptorSP();257return ObjCLanguageRuntime::ClassDescriptorSP(258new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp));259}260261AppleObjCRuntime::ClassDescriptorSP262AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const {263return ClassDescriptorSP();264}265266bool AppleObjCRuntimeV1::ClassDescriptorV1::Describe(267std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,268std::function<bool(const char *, const char *)> const &instance_method_func,269std::function<bool(const char *, const char *)> const &class_method_func,270std::function<bool(const char *, const char *, lldb::addr_t,271uint64_t)> const &ivar_func) const {272return false;273}274275lldb::addr_t AppleObjCRuntimeV1::GetTaggedPointerObfuscator() {276return 0;277}278279lldb::addr_t AppleObjCRuntimeV1::GetISAHashTablePointer() {280if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) {281ModuleSP objc_module_sp(GetObjCModule());282283if (!objc_module_sp)284return LLDB_INVALID_ADDRESS;285286static ConstString g_objc_debug_class_hash("_objc_debug_class_hash");287288const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(289g_objc_debug_class_hash, lldb::eSymbolTypeData);290if (symbol && symbol->ValueIsAddress()) {291Process *process = GetProcess();292if (process) {293294lldb::addr_t objc_debug_class_hash_addr =295symbol->GetAddressRef().GetLoadAddress(&process->GetTarget());296297if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) {298Status error;299lldb::addr_t objc_debug_class_hash_ptr =300process->ReadPointerFromMemory(objc_debug_class_hash_addr, error);301if (objc_debug_class_hash_ptr != 0 &&302objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS) {303m_isa_hash_table_ptr = objc_debug_class_hash_ptr;304}305}306}307}308}309return m_isa_hash_table_ptr;310}311312void AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() {313// TODO: implement HashTableSignature...314Process *process = GetProcess();315316if (process) {317// Update the process stop ID that indicates the last time we updated the318// map, whether it was successful or not.319m_isa_to_descriptor_stop_id = process->GetStopID();320321Log *log = GetLog(LLDBLog::Process);322323ProcessSP process_sp = process->shared_from_this();324325ModuleSP objc_module_sp(GetObjCModule());326327if (!objc_module_sp)328return;329330lldb::addr_t hash_table_ptr = GetISAHashTablePointer();331if (hash_table_ptr != LLDB_INVALID_ADDRESS) {332// Read the NXHashTable struct:333//334// typedef struct {335// const NXHashTablePrototype *prototype;336// unsigned count;337// unsigned nbBuckets;338// void *buckets;339// const void *info;340// } NXHashTable;341342Status error;343DataBufferHeap buffer(1024, 0);344if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) ==34520) {346const uint32_t addr_size = m_process->GetAddressByteSize();347const ByteOrder byte_order = m_process->GetByteOrder();348DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), byte_order,349addr_size);350lldb::offset_t offset = addr_size; // Skip prototype351const uint32_t count = data.GetU32(&offset);352const uint32_t num_buckets = data.GetU32(&offset);353const addr_t buckets_ptr = data.GetAddress(&offset);354if (m_hash_signature.NeedsUpdate(count, num_buckets, buckets_ptr)) {355m_hash_signature.UpdateSignature(count, num_buckets, buckets_ptr);356357const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t);358buffer.SetByteSize(data_size);359360if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size,361error) == data_size) {362data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order);363offset = 0;364for (uint32_t bucket_idx = 0; bucket_idx < num_buckets;365++bucket_idx) {366const uint32_t bucket_isa_count = data.GetU32(&offset);367const lldb::addr_t bucket_data = data.GetU32(&offset);368369if (bucket_isa_count == 0)370continue;371372ObjCISA isa;373if (bucket_isa_count == 1) {374// When we only have one entry in the bucket, the bucket data375// is the "isa"376isa = bucket_data;377if (isa) {378if (!ISAIsCached(isa)) {379ClassDescriptorSP descriptor_sp(380new ClassDescriptorV1(isa, process_sp));381382if (log && log->GetVerbose())383LLDB_LOGF(log,384"AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64385" from _objc_debug_class_hash to "386"isa->descriptor cache",387isa);388389AddClass(isa, descriptor_sp);390}391}392} else {393// When we have more than one entry in the bucket, the bucket394// data is a pointer to an array of "isa" values395addr_t isa_addr = bucket_data;396for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count;397++isa_idx, isa_addr += addr_size) {398isa = m_process->ReadPointerFromMemory(isa_addr, error);399400if (isa && isa != LLDB_INVALID_ADDRESS) {401if (!ISAIsCached(isa)) {402ClassDescriptorSP descriptor_sp(403new ClassDescriptorV1(isa, process_sp));404405if (log && log->GetVerbose())406LLDB_LOGF(407log,408"AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64409" from _objc_debug_class_hash to isa->descriptor "410"cache",411isa);412413AddClass(isa, descriptor_sp);414}415}416}417}418}419}420}421}422}423} else {424m_isa_to_descriptor_stop_id = UINT32_MAX;425}426}427428DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() {429return nullptr;430}431432433