2010-11-05 02:30:59 +08:00
|
|
|
//===-- AppleObjCRuntimeV1.cpp --------------------------------------*- C++ -*-===//
|
2010-09-23 10:01:19 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2010-11-05 02:30:59 +08:00
|
|
|
#include "AppleObjCRuntimeV1.h"
|
2010-09-28 09:25:32 +08:00
|
|
|
#include "AppleObjCTrampolineHandler.h"
|
2010-09-23 10:01:19 +08:00
|
|
|
|
2010-11-04 06:19:38 +08:00
|
|
|
#include "llvm/Support/MachO.h"
|
2010-09-30 08:54:27 +08:00
|
|
|
#include "clang/AST/Type.h"
|
|
|
|
|
2010-11-04 06:19:38 +08:00
|
|
|
#include "lldb/Breakpoint/BreakpointLocation.h"
|
2010-09-23 10:01:19 +08:00
|
|
|
#include "lldb/Core/ConstString.h"
|
|
|
|
#include "lldb/Core/Error.h"
|
2010-09-28 09:25:32 +08:00
|
|
|
#include "lldb/Core/Log.h"
|
2010-09-23 10:01:19 +08:00
|
|
|
#include "lldb/Core/Module.h"
|
|
|
|
#include "lldb/Core/PluginManager.h"
|
|
|
|
#include "lldb/Core/Scalar.h"
|
2010-09-28 09:25:32 +08:00
|
|
|
#include "lldb/Core/StreamString.h"
|
|
|
|
#include "lldb/Expression/ClangFunction.h"
|
2010-11-05 02:30:59 +08:00
|
|
|
#include "lldb/Expression/ClangUtilityFunction.h"
|
2010-09-23 10:01:19 +08:00
|
|
|
#include "lldb/Symbol/ClangASTContext.h"
|
2012-10-12 02:07:21 +08:00
|
|
|
#include "lldb/Symbol/Symbol.h"
|
2010-09-28 09:25:32 +08:00
|
|
|
#include "lldb/Target/ExecutionContext.h"
|
2010-09-23 10:01:19 +08:00
|
|
|
#include "lldb/Target/Process.h"
|
|
|
|
#include "lldb/Target/RegisterContext.h"
|
|
|
|
#include "lldb/Target/Target.h"
|
|
|
|
#include "lldb/Target/Thread.h"
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
using namespace lldb;
|
|
|
|
using namespace lldb_private;
|
|
|
|
|
2010-11-05 02:30:59 +08:00
|
|
|
static const char *pluginName = "AppleObjCRuntimeV1";
|
|
|
|
static const char *pluginDesc = "Apple Objective C Language Runtime - Version 1";
|
|
|
|
static const char *pluginShort = "language.apple.objc.v1";
|
2010-09-23 10:01:19 +08:00
|
|
|
|
2011-04-16 08:01:13 +08:00
|
|
|
bool
|
2011-05-04 11:43:18 +08:00
|
|
|
AppleObjCRuntimeV1::GetDynamicTypeAndAddress (ValueObject &in_value,
|
|
|
|
lldb::DynamicValueType use_dynamic,
|
|
|
|
TypeAndOrName &class_type_or_name,
|
|
|
|
Address &address)
|
2010-09-28 09:25:32 +08:00
|
|
|
{
|
2011-04-16 08:01:13 +08:00
|
|
|
return false;
|
2010-09-28 09:25:32 +08:00
|
|
|
}
|
|
|
|
|
2010-09-23 10:01:19 +08:00
|
|
|
//------------------------------------------------------------------
|
|
|
|
// Static Functions
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
lldb_private::LanguageRuntime *
|
2010-11-05 02:30:59 +08:00
|
|
|
AppleObjCRuntimeV1::CreateInstance (Process *process, lldb::LanguageType language)
|
2010-09-23 10:01:19 +08:00
|
|
|
{
|
|
|
|
// FIXME: This should be a MacOS or iOS process, and we need to look for the OBJC section to make
|
|
|
|
// sure we aren't using the V1 runtime.
|
|
|
|
if (language == eLanguageTypeObjC)
|
2010-11-05 02:30:59 +08:00
|
|
|
{
|
2010-11-05 08:57:06 +08:00
|
|
|
ModuleSP objc_module_sp;
|
|
|
|
|
2011-03-25 05:19:54 +08:00
|
|
|
if (AppleObjCRuntime::GetObjCVersion (process, objc_module_sp) == eAppleObjC_V1)
|
2010-11-05 02:30:59 +08:00
|
|
|
return new AppleObjCRuntimeV1 (process);
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-09-23 10:01:19 +08:00
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-11-05 02:30:59 +08:00
|
|
|
AppleObjCRuntimeV1::Initialize()
|
2010-09-23 10:01:19 +08:00
|
|
|
{
|
|
|
|
PluginManager::RegisterPlugin (pluginName,
|
|
|
|
pluginDesc,
|
|
|
|
CreateInstance);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-11-05 02:30:59 +08:00
|
|
|
AppleObjCRuntimeV1::Terminate()
|
2010-09-23 10:01:19 +08:00
|
|
|
{
|
|
|
|
PluginManager::UnregisterPlugin (CreateInstance);
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
// PluginInterface protocol
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
const char *
|
2010-11-05 02:30:59 +08:00
|
|
|
AppleObjCRuntimeV1::GetPluginName()
|
2010-09-23 10:01:19 +08:00
|
|
|
{
|
|
|
|
return pluginName;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
2010-11-05 02:30:59 +08:00
|
|
|
AppleObjCRuntimeV1::GetShortPluginName()
|
2010-09-23 10:01:19 +08:00
|
|
|
{
|
|
|
|
return pluginShort;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
2010-11-05 02:30:59 +08:00
|
|
|
AppleObjCRuntimeV1::GetPluginVersion()
|
2010-09-23 10:01:19 +08:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-03-05 12:47:34 +08:00
|
|
|
BreakpointResolverSP
|
|
|
|
AppleObjCRuntimeV1::CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp)
|
2010-11-04 06:19:38 +08:00
|
|
|
{
|
2012-03-05 12:47:34 +08:00
|
|
|
BreakpointResolverSP resolver_sp;
|
2012-03-03 10:05:11 +08:00
|
|
|
|
2012-03-06 08:37:27 +08:00
|
|
|
if (throw_bp)
|
2012-03-05 12:47:34 +08:00
|
|
|
resolver_sp.reset (new BreakpointResolverName (bkpt,
|
|
|
|
"objc_exception_throw",
|
|
|
|
eFunctionNameTypeBase,
|
|
|
|
Breakpoint::Exact,
|
|
|
|
eLazyBoolNo));
|
2012-03-06 08:37:27 +08:00
|
|
|
// FIXME: don't do catch yet.
|
2012-03-05 12:47:34 +08:00
|
|
|
return resolver_sp;
|
2010-11-04 06:19:38 +08:00
|
|
|
}
|
|
|
|
|
2010-11-05 08:57:06 +08:00
|
|
|
struct BufStruct {
|
|
|
|
char contents[2048];
|
|
|
|
};
|
|
|
|
|
2010-11-05 02:30:59 +08:00
|
|
|
ClangUtilityFunction *
|
|
|
|
AppleObjCRuntimeV1::CreateObjectChecker(const char *name)
|
|
|
|
{
|
2010-11-05 08:57:06 +08:00
|
|
|
std::auto_ptr<BufStruct> buf(new BufStruct);
|
|
|
|
|
|
|
|
assert(snprintf(&buf->contents[0], sizeof(buf->contents),
|
|
|
|
"struct __objc_class \n"
|
|
|
|
"{ \n"
|
|
|
|
" struct __objc_class *isa; \n"
|
|
|
|
" struct __objc_class *super_class; \n"
|
|
|
|
" const char *name; \n"
|
|
|
|
" // rest of struct elided because unused \n"
|
|
|
|
"}; \n"
|
|
|
|
" \n"
|
|
|
|
"struct __objc_object \n"
|
|
|
|
"{ \n"
|
|
|
|
" struct __objc_class *isa; \n"
|
|
|
|
"}; \n"
|
|
|
|
" \n"
|
|
|
|
"extern \"C\" void \n"
|
2011-10-27 08:02:05 +08:00
|
|
|
"%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) \n"
|
2010-11-05 08:57:06 +08:00
|
|
|
"{ \n"
|
|
|
|
" struct __objc_object *obj = (struct __objc_object*)$__lldb_arg_obj; \n"
|
2011-05-20 11:15:54 +08:00
|
|
|
" (int)strlen(obj->isa->name); \n"
|
2010-11-05 08:57:06 +08:00
|
|
|
"} \n",
|
|
|
|
name) < sizeof(buf->contents));
|
|
|
|
|
|
|
|
return new ClangUtilityFunction(buf->contents, name);
|
2010-11-04 06:19:38 +08:00
|
|
|
}
|
2012-09-05 02:47:54 +08:00
|
|
|
|
|
|
|
// this code relies on the assumption that an Objective-C object always starts
|
|
|
|
// with an ISA at offset 0.
|
2012-10-12 02:07:21 +08:00
|
|
|
//ObjCLanguageRuntime::ObjCISA
|
|
|
|
//AppleObjCRuntimeV1::GetISA(ValueObject& valobj)
|
|
|
|
//{
|
|
|
|
//// if (ClangASTType::GetMinimumLanguage(valobj.GetClangAST(),valobj.GetClangType()) != eLanguageTypeObjC)
|
|
|
|
//// return 0;
|
|
|
|
//
|
|
|
|
// // if we get an invalid VO (which might still happen when playing around
|
|
|
|
// // with pointers returned by the expression parser, don't consider this
|
|
|
|
// // a valid ObjC object)
|
|
|
|
// if (valobj.GetValue().GetContextType() == Value::eContextTypeInvalid)
|
2012-10-10 01:51:53 +08:00
|
|
|
// return 0;
|
2012-10-12 02:07:21 +08:00
|
|
|
//
|
|
|
|
// addr_t isa_pointer = valobj.GetPointerValue();
|
|
|
|
//
|
|
|
|
// ExecutionContext exe_ctx (valobj.GetExecutionContextRef());
|
|
|
|
//
|
|
|
|
// Process *process = exe_ctx.GetProcessPtr();
|
|
|
|
// if (process)
|
|
|
|
// {
|
|
|
|
// uint8_t pointer_size = process->GetAddressByteSize();
|
|
|
|
//
|
|
|
|
// Error error;
|
|
|
|
// return process->ReadUnsignedIntegerFromMemory (isa_pointer,
|
|
|
|
// pointer_size,
|
|
|
|
// 0,
|
|
|
|
// error);
|
|
|
|
// }
|
|
|
|
// return 0;
|
|
|
|
//}
|
2012-09-05 02:47:54 +08:00
|
|
|
|
|
|
|
AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ValueObject &isa_pointer)
|
|
|
|
{
|
|
|
|
ObjCISA ptr_value = isa_pointer.GetValueAsUnsigned(0);
|
|
|
|
|
|
|
|
lldb::ProcessSP process_sp = isa_pointer.GetProcessSP();
|
|
|
|
|
|
|
|
Initialize (ptr_value,process_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1 (ObjCISA isa, lldb::ProcessSP process_sp)
|
|
|
|
{
|
|
|
|
Initialize (isa, process_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AppleObjCRuntimeV1::ClassDescriptorV1::Initialize (ObjCISA isa, lldb::ProcessSP process_sp)
|
|
|
|
{
|
|
|
|
if (!isa || !process_sp)
|
|
|
|
{
|
|
|
|
m_valid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_valid = true;
|
|
|
|
|
|
|
|
Error error;
|
|
|
|
|
|
|
|
m_isa = process_sp->ReadPointerFromMemory(isa, error);
|
|
|
|
|
|
|
|
if (error.Fail())
|
|
|
|
{
|
|
|
|
m_valid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t ptr_size = process_sp->GetAddressByteSize();
|
|
|
|
|
|
|
|
if (!IsPointerValid(m_isa,ptr_size))
|
|
|
|
{
|
|
|
|
m_valid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size,error);
|
|
|
|
|
|
|
|
if (error.Fail())
|
|
|
|
{
|
|
|
|
m_valid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!IsPointerValid(m_parent_isa,ptr_size,true))
|
|
|
|
{
|
|
|
|
m_valid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
lldb::addr_t name_ptr = process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size,error);
|
|
|
|
|
|
|
|
if (error.Fail())
|
|
|
|
{
|
|
|
|
m_valid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
|
|
|
|
|
|
|
|
size_t count = process_sp->ReadCStringFromMemory(name_ptr, (char*)buffer_sp->GetBytes(), 1024, error);
|
|
|
|
|
|
|
|
if (error.Fail())
|
|
|
|
{
|
|
|
|
m_valid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count)
|
|
|
|
m_name = ConstString((char*)buffer_sp->GetBytes());
|
|
|
|
else
|
|
|
|
m_name = ConstString();
|
|
|
|
|
|
|
|
m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(m_isa + 5 * ptr_size, ptr_size, 0, error);
|
|
|
|
|
|
|
|
if (error.Fail())
|
|
|
|
{
|
|
|
|
m_valid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_process_wp = lldb::ProcessWP(process_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
AppleObjCRuntime::ClassDescriptorSP
|
|
|
|
AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass ()
|
|
|
|
{
|
|
|
|
if (!m_valid)
|
2012-09-07 01:10:14 +08:00
|
|
|
return AppleObjCRuntime::ClassDescriptorSP();
|
2012-09-05 02:47:54 +08:00
|
|
|
ProcessSP process_sp = m_process_wp.lock();
|
|
|
|
if (!process_sp)
|
2012-09-07 01:10:14 +08:00
|
|
|
return AppleObjCRuntime::ClassDescriptorSP();
|
2012-09-05 02:47:54 +08:00
|
|
|
return ObjCLanguageRuntime::ClassDescriptorSP(new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa,process_sp));
|
|
|
|
}
|
|
|
|
|
2012-10-12 02:07:21 +08:00
|
|
|
bool
|
|
|
|
AppleObjCRuntimeV1::UpdateISAToDescriptorMap_Impl()
|
2012-09-05 02:47:54 +08:00
|
|
|
{
|
2012-10-12 02:07:21 +08:00
|
|
|
lldb::LogSP log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
|
|
|
|
Process *process_ptr = GetProcess();
|
|
|
|
|
|
|
|
if (!process_ptr)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ProcessSP process_sp = process_ptr->shared_from_this();
|
|
|
|
|
|
|
|
ModuleSP objc_module_sp(GetObjCModule());
|
|
|
|
|
|
|
|
if (!objc_module_sp)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
uint32_t isa_count = 0;
|
|
|
|
|
|
|
|
static ConstString g_objc_debug_class_hash("_objc_debug_class_hash");
|
|
|
|
|
|
|
|
const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(g_objc_debug_class_hash, lldb::eSymbolTypeData);
|
|
|
|
if (symbol)
|
|
|
|
{
|
|
|
|
lldb::addr_t objc_debug_class_hash_addr = symbol->GetAddress().GetLoadAddress(&process_sp->GetTarget());
|
|
|
|
|
|
|
|
if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS)
|
|
|
|
{
|
|
|
|
Error error;
|
|
|
|
lldb::addr_t objc_debug_class_hash_ptr = process_sp->ReadPointerFromMemory(objc_debug_class_hash_addr, error);
|
|
|
|
if (error.Success() && objc_debug_class_hash_ptr != 0 && objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS)
|
|
|
|
{
|
|
|
|
// Read the NXHashTable struct:
|
|
|
|
//
|
|
|
|
// typedef struct {
|
|
|
|
// const NXHashTablePrototype *prototype;
|
|
|
|
// unsigned count;
|
|
|
|
// unsigned nbBuckets;
|
|
|
|
// void *buckets;
|
|
|
|
// const void *info;
|
|
|
|
// } NXHashTable;
|
|
|
|
|
|
|
|
DataBufferHeap buffer(1024, 0);
|
|
|
|
if (process_sp->ReadMemory(objc_debug_class_hash_ptr, buffer.GetBytes(), 20, error) == 20)
|
|
|
|
{
|
|
|
|
const uint32_t addr_size = m_process->GetAddressByteSize();
|
|
|
|
const ByteOrder byte_order = m_process->GetByteOrder();
|
|
|
|
DataExtractor data (buffer.GetBytes(), buffer.GetByteSize(), byte_order, addr_size);
|
|
|
|
uint32_t offset = addr_size + 4; // Skip prototype
|
|
|
|
const uint32_t num_buckets = data.GetU32(&offset);
|
|
|
|
const addr_t buckets_ptr = data.GetPointer(&offset);
|
|
|
|
|
|
|
|
const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t);
|
|
|
|
buffer.SetByteSize(data_size);
|
|
|
|
|
|
|
|
if (process_sp->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size, error) == data_size)
|
|
|
|
{
|
|
|
|
data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order);
|
|
|
|
offset = 0;
|
|
|
|
for (uint32_t bucket_idx = 0; bucket_idx < num_buckets; ++bucket_idx)
|
|
|
|
{
|
|
|
|
const uint32_t bucket_isa_count = data.GetU32 (&offset);
|
|
|
|
const lldb::addr_t bucket_data = data.GetU32 (&offset);
|
|
|
|
|
|
|
|
|
|
|
|
if (bucket_isa_count == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
isa_count += bucket_isa_count;
|
|
|
|
|
|
|
|
ObjCISA isa;
|
|
|
|
if (bucket_isa_count == 1)
|
|
|
|
{
|
|
|
|
// When we only have one entry in the bucket, the bucket data is the "isa"
|
|
|
|
isa = bucket_data;
|
|
|
|
if (isa)
|
|
|
|
{
|
|
|
|
if (m_isa_to_descriptor_cache.count(isa) == 0)
|
|
|
|
{
|
|
|
|
ClassDescriptorSP descriptor_sp (new ClassDescriptorV1(isa, process_sp));
|
|
|
|
|
|
|
|
if (log && log->GetVerbose())
|
|
|
|
log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%llx from _objc_debug_class_hash to isa->descriptor cache", isa);
|
|
|
|
|
|
|
|
m_isa_to_descriptor_cache[isa] = descriptor_sp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// When we have more than one entry in the bucket, the bucket data is a pointer
|
|
|
|
// to an array of "isa" values
|
|
|
|
addr_t isa_addr = bucket_data;
|
|
|
|
for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count; ++isa_idx, isa_addr += addr_size)
|
|
|
|
{
|
|
|
|
isa = m_process->ReadPointerFromMemory(isa_addr, error);
|
|
|
|
|
|
|
|
if (isa && isa != LLDB_INVALID_ADDRESS)
|
|
|
|
{
|
|
|
|
if (m_isa_to_descriptor_cache.count(isa) == 0)
|
|
|
|
{
|
|
|
|
ClassDescriptorSP descriptor_sp (new ClassDescriptorV1(isa, process_sp));
|
|
|
|
|
|
|
|
if (log && log->GetVerbose())
|
|
|
|
log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%llx from _objc_debug_class_hash to isa->descriptor cache", isa);
|
|
|
|
|
|
|
|
m_isa_to_descriptor_cache[isa] = descriptor_sp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return isa_count > 0;
|
2012-09-05 02:47:54 +08:00
|
|
|
}
|
2012-10-12 02:07:21 +08:00
|
|
|
|