forked from OSchip/llvm-project
Handle stepping through ObjC vtable trampoline code.
llvm-svn: 118270
This commit is contained in:
parent
7da7cc568f
commit
5822173bc8
|
@ -15,7 +15,9 @@
|
|||
// Project includes
|
||||
#include "AppleThreadPlanStepThroughObjCTrampoline.h"
|
||||
|
||||
#include "lldb/Breakpoint/StoppointCallbackContext.h"
|
||||
#include "lldb/Core/ConstString.h"
|
||||
#include "lldb/Core/Debugger.h"
|
||||
#include "lldb/Core/FileSpec.h"
|
||||
#include "lldb/Core/Log.h"
|
||||
#include "lldb/Core/Module.h"
|
||||
|
@ -33,6 +35,351 @@
|
|||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
||||
|
||||
AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr) :
|
||||
m_valid (true),
|
||||
m_owner(owner),
|
||||
m_header_addr (header_addr),
|
||||
m_code_start_addr(0),
|
||||
m_code_end_addr (0),
|
||||
m_next_region (0)
|
||||
{
|
||||
SetUpRegion ();
|
||||
}
|
||||
|
||||
void
|
||||
AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::SetUpRegion()
|
||||
{
|
||||
// The header looks like:
|
||||
//
|
||||
// uint16_t headerSize
|
||||
// uint16_t descSize
|
||||
// uint32_t descCount
|
||||
// void * next
|
||||
//
|
||||
// First read in the header:
|
||||
|
||||
char memory_buffer[16];
|
||||
Process *process = m_owner->GetProcess();
|
||||
DataExtractor data(memory_buffer, sizeof(memory_buffer),
|
||||
process->GetByteOrder(),
|
||||
process->GetAddressByteSize());
|
||||
size_t actual_size = 8 + process->GetAddressByteSize();
|
||||
Error error;
|
||||
size_t bytes_read = process->ReadMemory (m_header_addr, memory_buffer, actual_size, error);
|
||||
if (bytes_read != actual_size)
|
||||
{
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t offset_ptr = 0;
|
||||
uint16_t header_size = data.GetU16(&offset_ptr);
|
||||
uint16_t descriptor_size = data.GetU16(&offset_ptr);
|
||||
size_t num_descriptors = data.GetU32(&offset_ptr);
|
||||
|
||||
m_next_region = data.GetPointer(&offset_ptr);
|
||||
|
||||
// If the header size is 0, that means we've come in too early before this data is set up.
|
||||
// Set ourselves as not valid, and continue.
|
||||
if (header_size == 0)
|
||||
{
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Now read in all the descriptors:
|
||||
// The descriptor looks like:
|
||||
//
|
||||
// uint32_t offset
|
||||
// uint32_t flags
|
||||
//
|
||||
// Where offset is either 0 - in which case it is unused, or
|
||||
// it is the offset of the vtable code from the beginning of the descriptor record.
|
||||
// Below, we'll convert that into an absolute code address, since I don't want to have
|
||||
// to compute it over and over.
|
||||
|
||||
// Ingest the whole descriptor array:
|
||||
lldb::addr_t desc_ptr = m_header_addr + header_size;
|
||||
size_t desc_array_size = num_descriptors * descriptor_size;
|
||||
DataBufferSP data_sp(new DataBufferHeap (desc_array_size, '\0'));
|
||||
uint8_t* dst = (uint8_t*)data_sp->GetBytes();
|
||||
|
||||
DataExtractor desc_extractor (dst, desc_array_size,
|
||||
process->GetByteOrder(),
|
||||
process->GetAddressByteSize());
|
||||
bytes_read = process->ReadMemory(desc_ptr, dst, desc_array_size, error);
|
||||
if (bytes_read != desc_array_size)
|
||||
{
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// The actual code for the vtables will be laid out consecutively, so I also
|
||||
// compute the start and end of the whole code block.
|
||||
|
||||
offset_ptr = 0;
|
||||
m_code_start_addr = 0;
|
||||
m_code_end_addr = 0;
|
||||
|
||||
for (int i = 0; i < num_descriptors; i++)
|
||||
{
|
||||
lldb::addr_t start_offset = offset_ptr;
|
||||
uint32_t offset = desc_extractor.GetU32 (&offset_ptr);
|
||||
uint32_t flags = desc_extractor.GetU32 (&offset_ptr);
|
||||
lldb:addr_t code_addr = desc_ptr + start_offset + offset;
|
||||
m_descriptors.push_back (VTableDescriptor(flags, code_addr));
|
||||
|
||||
if (m_code_start_addr == 0 || code_addr < m_code_start_addr)
|
||||
m_code_start_addr = code_addr;
|
||||
if (code_addr > m_code_end_addr)
|
||||
m_code_end_addr = code_addr;
|
||||
|
||||
offset_ptr = start_offset + descriptor_size;
|
||||
}
|
||||
// Finally, a little bird told me that all the vtable code blocks are the same size.
|
||||
// Let's compute the blocks and if they are all the same add the size to the code end address:
|
||||
lldb::addr_t code_size = 0;
|
||||
bool all_the_same = true;
|
||||
for (int i = 0; i < num_descriptors - 1; i++)
|
||||
{
|
||||
lldb::addr_t this_size = m_descriptors[i + 1].code_start - m_descriptors[i].code_start;
|
||||
if (code_size == 0)
|
||||
code_size = this_size;
|
||||
else
|
||||
{
|
||||
if (this_size != code_size)
|
||||
all_the_same = false;
|
||||
if (this_size > code_size)
|
||||
code_size = this_size;
|
||||
}
|
||||
}
|
||||
if (all_the_same)
|
||||
m_code_end_addr += code_size;
|
||||
}
|
||||
|
||||
bool
|
||||
AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::AddressInRegion (lldb::addr_t addr, uint32_t &flags)
|
||||
{
|
||||
if (!IsValid())
|
||||
return false;
|
||||
|
||||
if (addr < m_code_start_addr || addr > m_code_end_addr)
|
||||
return false;
|
||||
|
||||
std::vector<VTableDescriptor>::iterator pos, end = m_descriptors.end();
|
||||
for (pos = m_descriptors.begin(); pos != end; pos++)
|
||||
{
|
||||
if (addr <= (*pos).code_start)
|
||||
{
|
||||
flags = (*pos).flags;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::Dump (Stream &s)
|
||||
{
|
||||
s.Printf ("Header addr: 0x%llx Code start: 0x%llx Code End: 0x%llx Next: 0x%llx\n",
|
||||
m_header_addr, m_code_start_addr, m_code_end_addr, m_next_region);
|
||||
size_t num_elements = m_descriptors.size();
|
||||
for (size_t i = 0; i < num_elements; i++)
|
||||
{
|
||||
s.Indent();
|
||||
s.Printf ("Code start: 0x%llx Flags: %d\n", m_descriptors[i].code_start, m_descriptors[i].flags);
|
||||
}
|
||||
}
|
||||
|
||||
AppleObjCTrampolineHandler::AppleObjCVTables::AppleObjCVTables (ProcessSP &process_sp, ModuleSP &objc_module_sp) :
|
||||
m_process_sp(process_sp),
|
||||
m_trampoline_header(LLDB_INVALID_ADDRESS),
|
||||
m_trampolines_changed_bp_id(LLDB_INVALID_BREAK_ID),
|
||||
m_objc_module_sp(objc_module_sp)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AppleObjCTrampolineHandler::AppleObjCVTables::~AppleObjCVTables()
|
||||
{
|
||||
if (m_trampolines_changed_bp_id != LLDB_INVALID_BREAK_ID)
|
||||
m_process_sp->GetTarget().RemoveBreakpointByID (m_trampolines_changed_bp_id);
|
||||
}
|
||||
|
||||
bool
|
||||
AppleObjCTrampolineHandler::AppleObjCVTables::InitializeVTableSymbols ()
|
||||
{
|
||||
if (m_trampoline_header != LLDB_INVALID_ADDRESS)
|
||||
return true;
|
||||
Target &target = m_process_sp->GetTarget();
|
||||
|
||||
ModuleList &modules = target.GetImages();
|
||||
size_t num_modules = modules.GetSize();
|
||||
if (!m_objc_module_sp)
|
||||
{
|
||||
for (size_t i = 0; i < num_modules; i++)
|
||||
{
|
||||
if (m_process_sp->GetObjCLanguageRuntime()->IsModuleObjCLibrary (modules.GetModuleAtIndex(i)))
|
||||
{
|
||||
m_objc_module_sp = modules.GetModuleAtIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_objc_module_sp)
|
||||
{
|
||||
ConstString trampoline_name ("gdb_objc_trampolines");
|
||||
const Symbol *trampoline_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType(trampoline_name,
|
||||
eSymbolTypeData);
|
||||
if (trampoline_symbol != NULL)
|
||||
{
|
||||
const Address &temp_address = trampoline_symbol->GetValue();
|
||||
if (!temp_address.IsValid())
|
||||
return false;
|
||||
|
||||
m_trampoline_header = temp_address.GetLoadAddress(&target);
|
||||
if (m_trampoline_header == LLDB_INVALID_ADDRESS)
|
||||
return false;
|
||||
|
||||
// Next look up the "changed" symbol and set a breakpoint on that...
|
||||
ConstString changed_name ("gdb_objc_trampolines_changed");
|
||||
const Symbol *changed_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType(changed_name,
|
||||
eSymbolTypeCode);
|
||||
if (changed_symbol != NULL)
|
||||
{
|
||||
const Address &temp_address = changed_symbol->GetValue();
|
||||
if (!temp_address.IsValid())
|
||||
return false;
|
||||
|
||||
lldb::addr_t changed_addr = temp_address.GetLoadAddress(&target);
|
||||
if (changed_addr != LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
BreakpointSP trampolines_changed_bp_sp = target.CreateBreakpoint (changed_addr,
|
||||
true);
|
||||
if (trampolines_changed_bp_sp != NULL)
|
||||
{
|
||||
m_trampolines_changed_bp_id = trampolines_changed_bp_sp->GetID();
|
||||
trampolines_changed_bp_sp->SetCallback (RefreshTrampolines, this, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines (void *baton,
|
||||
StoppointCallbackContext *context,
|
||||
lldb::user_id_t break_id,
|
||||
lldb::user_id_t break_loc_id)
|
||||
{
|
||||
AppleObjCVTables *vtable_handler = (AppleObjCVTables *) baton;
|
||||
if (vtable_handler->InitializeVTableSymbols())
|
||||
{
|
||||
// The Update function is called with the address of an added region. So we grab that address, and
|
||||
// feed it into ReadRegions. Of course, our friend the ABI will get the values for us.
|
||||
Process *process = context->exe_ctx.process;
|
||||
const ABI *abi = process->GetABI();
|
||||
|
||||
ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext();
|
||||
ValueList argument_values;
|
||||
Value input_value;
|
||||
void *clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false);
|
||||
input_value.SetValueType (Value::eValueTypeScalar);
|
||||
input_value.SetContext (Value::eContextTypeOpaqueClangQualType, clang_void_ptr_type);
|
||||
argument_values.PushValue(input_value);
|
||||
|
||||
bool success = abi->GetArgumentValues (*(context->exe_ctx.thread), argument_values);
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
// Now get a pointer value from the zeroth argument.
|
||||
Error error;
|
||||
DataExtractor data;
|
||||
error = argument_values.GetValueAtIndex(0)->GetValueAsData(&(context->exe_ctx), clang_ast_context->getASTContext(), data, 0);
|
||||
uint32_t offset_ptr = 0;
|
||||
lldb::addr_t region_addr = data.GetPointer(&offset_ptr);
|
||||
|
||||
if (region_addr != 0)
|
||||
vtable_handler->ReadRegions(region_addr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions ()
|
||||
{
|
||||
// The no argument version reads the start region from the value of the gdb_regions_header, and
|
||||
// gets started from there.
|
||||
|
||||
m_regions.clear();
|
||||
if (!InitializeVTableSymbols())
|
||||
return false;
|
||||
char memory_buffer[8];
|
||||
DataExtractor data(memory_buffer, sizeof(memory_buffer),
|
||||
m_process_sp->GetByteOrder(),
|
||||
m_process_sp->GetAddressByteSize());
|
||||
Error error;
|
||||
size_t bytes_read = m_process_sp->ReadMemory (m_trampoline_header, memory_buffer, m_process_sp->GetAddressByteSize(), error);
|
||||
if (bytes_read != m_process_sp->GetAddressByteSize())
|
||||
return false;
|
||||
|
||||
uint32_t offset_ptr = 0;
|
||||
lldb::addr_t region_addr = data.GetPointer(&offset_ptr);
|
||||
return ReadRegions (region_addr);
|
||||
}
|
||||
|
||||
bool
|
||||
AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions (lldb::addr_t region_addr)
|
||||
{
|
||||
if (!m_process_sp)
|
||||
return false;
|
||||
|
||||
Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP);
|
||||
|
||||
// We aren't starting at the trampoline symbol.
|
||||
InitializeVTableSymbols ();
|
||||
lldb::addr_t next_region = region_addr;
|
||||
|
||||
// Read in the sizes of the headers.
|
||||
while (next_region != 0)
|
||||
{
|
||||
m_regions.push_back (VTableRegion(this, next_region));
|
||||
if (!m_regions.back().IsValid())
|
||||
{
|
||||
m_regions.clear();
|
||||
return false;
|
||||
}
|
||||
if (log)
|
||||
{
|
||||
StreamString s;
|
||||
m_regions.back().Dump(s);
|
||||
log->Printf("Read vtable region: \n%s", s.GetData());
|
||||
}
|
||||
|
||||
next_region = m_regions.back().GetNextRegionAddr();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AppleObjCTrampolineHandler::AppleObjCVTables::IsAddressInVTables (lldb::addr_t addr, uint32_t &flags)
|
||||
{
|
||||
region_collection::iterator pos, end = m_regions.end();
|
||||
for (pos = m_regions.begin(); pos != end; pos++)
|
||||
{
|
||||
if ((*pos).AddressInRegion (addr, flags))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const AppleObjCTrampolineHandler::DispatchFunction
|
||||
AppleObjCTrampolineHandler::g_dispatch_functions[] =
|
||||
{
|
||||
|
@ -60,24 +407,9 @@ AppleObjCTrampolineHandler::g_dispatch_functions[] =
|
|||
{NULL}
|
||||
};
|
||||
|
||||
bool
|
||||
AppleObjCTrampolineHandler::ModuleIsObjCLibrary (const ModuleSP &module_sp)
|
||||
{
|
||||
const FileSpec &module_file_spec = module_sp->GetFileSpec();
|
||||
static ConstString ObjCName ("libobjc.A.dylib");
|
||||
|
||||
if (module_file_spec)
|
||||
{
|
||||
if (module_file_spec.GetFilename() == ObjCName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module) :
|
||||
AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module_sp) :
|
||||
m_process_sp (process_sp),
|
||||
m_objc_module_sp (objc_module),
|
||||
m_objc_module_sp (objc_module_sp),
|
||||
m_impl_fn_addr (LLDB_INVALID_ADDRESS),
|
||||
m_impl_stret_fn_addr (LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
|
@ -119,6 +451,11 @@ AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (ProcessSP process_sp, Mo
|
|||
m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i));
|
||||
}
|
||||
}
|
||||
|
||||
// Build our vtable dispatch handler here:
|
||||
m_vtables_ap.reset(new AppleObjCVTables(process_sp, m_objc_module_sp));
|
||||
if (m_vtables_ap.get())
|
||||
m_vtables_ap->ReadRegions();
|
||||
}
|
||||
|
||||
ThreadPlanSP
|
||||
|
@ -127,13 +464,38 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto
|
|||
ThreadPlanSP ret_plan_sp;
|
||||
lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();
|
||||
|
||||
DispatchFunction this_dispatch;
|
||||
bool found_it = false;
|
||||
|
||||
MsgsendMap::iterator pos;
|
||||
pos = m_msgSend_map.find (curr_pc);
|
||||
if (pos != m_msgSend_map.end())
|
||||
{
|
||||
this_dispatch = g_dispatch_functions[(*pos).second];
|
||||
found_it = true;
|
||||
}
|
||||
|
||||
if (!found_it)
|
||||
{
|
||||
uint32_t flags;
|
||||
if (m_vtables_ap.get())
|
||||
{
|
||||
found_it = m_vtables_ap->IsAddressInVTables (curr_pc, flags);
|
||||
if (found_it)
|
||||
{
|
||||
this_dispatch.name = "vtable";
|
||||
this_dispatch.stret_return
|
||||
= (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) == AppleObjCVTables::eOBJC_TRAMPOLINE_STRET;
|
||||
this_dispatch.is_super = false;
|
||||
this_dispatch.fixedup = DispatchFunction::eFixUpFixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_it)
|
||||
{
|
||||
Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP);
|
||||
|
||||
const DispatchFunction *this_dispatch = &g_dispatch_functions[(*pos).second];
|
||||
|
||||
lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
|
||||
|
||||
|
@ -161,7 +523,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto
|
|||
// If this is a struct return dispatch, then the first argument is the
|
||||
// return struct pointer, and the object is the second, and the selector is the third.
|
||||
// Otherwise the object is the first and the selector the second.
|
||||
if (this_dispatch->stret_return)
|
||||
if (this_dispatch.stret_return)
|
||||
{
|
||||
obj_index = 1;
|
||||
sel_index = 2;
|
||||
|
@ -197,7 +559,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto
|
|||
isa_value.SetValueType(Value::eValueTypeLoadAddress);
|
||||
isa_value.ResolveValue(&exec_ctx, clang_ast_context->getASTContext());
|
||||
|
||||
if (this_dispatch->fixedup == DispatchFunction::eFixUpFixed)
|
||||
if (this_dispatch.fixedup == DispatchFunction::eFixUpFixed)
|
||||
{
|
||||
// For the FixedUp method the Selector is actually a pointer to a
|
||||
// structure, the second field of which is the selector number.
|
||||
|
@ -206,7 +568,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto
|
|||
sel_value->SetValueType(Value::eValueTypeLoadAddress);
|
||||
sel_value->ResolveValue(&exec_ctx, clang_ast_context->getASTContext());
|
||||
}
|
||||
else if (this_dispatch->fixedup == DispatchFunction::eFixUpToFix)
|
||||
else if (this_dispatch.fixedup == DispatchFunction::eFixUpToFix)
|
||||
{
|
||||
// FIXME: If the method dispatch is not "fixed up" then the selector is actually a
|
||||
// pointer to the string name of the selector. We need to look that up...
|
||||
|
@ -219,7 +581,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto
|
|||
// FIXME: If this is a dispatch to the super-class, we need to get the super-class from
|
||||
// the class, and disaptch to that instead.
|
||||
// But for now I just punt and return no plan.
|
||||
if (this_dispatch->is_super)
|
||||
if (this_dispatch.is_super)
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("Punting on stepping into super method dispatch.");
|
||||
|
@ -244,7 +606,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto
|
|||
if (impl_addr == LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
|
||||
Address resolve_address(NULL, this_dispatch->stret_return ? m_impl_stret_fn_addr : m_impl_fn_addr);
|
||||
Address resolve_address(NULL, this_dispatch.stret_return ? m_impl_stret_fn_addr : m_impl_fn_addr);
|
||||
|
||||
StreamString errors;
|
||||
{
|
||||
|
@ -289,6 +651,12 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto
|
|||
dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
|
||||
dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong(),
|
||||
stop_others));
|
||||
if (log)
|
||||
{
|
||||
StreamString s;
|
||||
ret_plan_sp->GetDescription(&s, eDescriptionLevelFull);
|
||||
log->Printf("Using ObjC step plan: %s.\n", s.GetData());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -31,9 +31,7 @@ public:
|
|||
AppleObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module_sp);
|
||||
|
||||
~AppleObjCTrampolineHandler() {}
|
||||
|
||||
static bool ModuleIsObjCLibrary (const ModuleSP &module_sp);
|
||||
|
||||
|
||||
ThreadPlanSP
|
||||
GetStepThroughDispatchPlan (Thread &thread, bool stop_others);
|
||||
|
||||
|
@ -57,6 +55,127 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
|
||||
class AppleObjCVTables
|
||||
{
|
||||
public:
|
||||
// These come from objc-gdb.h.
|
||||
enum VTableFlags
|
||||
{
|
||||
eOBJC_TRAMPOLINE_MESSAGE = (1<<0), // trampoline acts like objc_msgSend
|
||||
eOBJC_TRAMPOLINE_STRET = (1<<1), // trampoline is struct-returning
|
||||
eOBJC_TRAMPOLINE_VTABLE = (1<<2), // trampoline is vtable dispatcher
|
||||
};
|
||||
|
||||
private:
|
||||
struct VTableDescriptor
|
||||
{
|
||||
VTableDescriptor(uint32_t in_flags, addr_t in_code_start) :
|
||||
flags(in_flags),
|
||||
code_start(in_code_start) {}
|
||||
|
||||
uint32_t flags;
|
||||
lldb::addr_t code_start;
|
||||
};
|
||||
|
||||
|
||||
class VTableRegion
|
||||
{
|
||||
public:
|
||||
VTableRegion() :
|
||||
m_valid (false),
|
||||
m_owner (NULL),
|
||||
m_header_addr (LLDB_INVALID_ADDRESS),
|
||||
m_code_start_addr(0),
|
||||
m_code_end_addr (0),
|
||||
m_next_region (0)
|
||||
{}
|
||||
|
||||
VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr);
|
||||
|
||||
void SetUpRegion();
|
||||
|
||||
lldb::addr_t GetNextRegionAddr ()
|
||||
{
|
||||
return m_next_region;
|
||||
}
|
||||
|
||||
lldb::addr_t
|
||||
GetCodeStart ()
|
||||
{
|
||||
return m_code_start_addr;
|
||||
}
|
||||
|
||||
lldb::addr_t
|
||||
GetCodeEnd ()
|
||||
{
|
||||
return m_code_end_addr;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GetFlagsForVTableAtAddress (lldb::addr_t address)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
IsValid ()
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
bool
|
||||
AddressInRegion (lldb::addr_t addr, uint32_t &flags);
|
||||
|
||||
void
|
||||
Dump (Stream &s);
|
||||
|
||||
public:
|
||||
bool m_valid;
|
||||
AppleObjCVTables *m_owner;
|
||||
lldb::addr_t m_header_addr;
|
||||
lldb::addr_t m_code_start_addr;
|
||||
lldb::addr_t m_code_end_addr;
|
||||
std::vector<VTableDescriptor> m_descriptors;
|
||||
lldb::addr_t m_next_region;
|
||||
};
|
||||
|
||||
public:
|
||||
AppleObjCVTables(ProcessSP &process_sp, ModuleSP &objc_module_sp);
|
||||
|
||||
~AppleObjCVTables();
|
||||
|
||||
bool
|
||||
InitializeVTableSymbols ();
|
||||
|
||||
static bool RefreshTrampolines (void *baton,
|
||||
StoppointCallbackContext *context,
|
||||
lldb::user_id_t break_id,
|
||||
lldb::user_id_t break_loc_id);
|
||||
bool
|
||||
ReadRegions ();
|
||||
|
||||
bool
|
||||
ReadRegions (lldb::addr_t region_addr);
|
||||
|
||||
bool
|
||||
IsAddressInVTables (lldb::addr_t addr, uint32_t &flags);
|
||||
|
||||
Process *GetProcess ()
|
||||
{
|
||||
return m_process_sp.get();
|
||||
}
|
||||
|
||||
private:
|
||||
ProcessSP m_process_sp;
|
||||
typedef std::vector<VTableRegion> region_collection;
|
||||
lldb::addr_t m_trampoline_header;
|
||||
lldb::break_id_t m_trampolines_changed_bp_id;
|
||||
region_collection m_regions;
|
||||
lldb::ModuleSP m_objc_module_sp;
|
||||
|
||||
};
|
||||
|
||||
static const DispatchFunction g_dispatch_functions[];
|
||||
|
||||
typedef std::map<lldb::addr_t, int> MsgsendMap; // This table maps an dispatch fn address to the index in g_dispatch_functions
|
||||
|
@ -68,6 +187,7 @@ private:
|
|||
Mutex m_impl_function_mutex;
|
||||
lldb::addr_t m_impl_fn_addr;
|
||||
lldb::addr_t m_impl_stret_fn_addr;
|
||||
std::auto_ptr<AppleObjCVTables> m_vtables_ap;
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -115,6 +115,13 @@ AppleThreadPlanStepThroughObjCTrampoline::ShouldStop (Event *event_ptr)
|
|||
lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong();
|
||||
Address target_address(NULL, target_addr);
|
||||
Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP);
|
||||
if (target_addr == 0)
|
||||
{
|
||||
if (log)
|
||||
log->Printf("Got target implementation of 0x0, stopping.");
|
||||
SetPlanComplete();
|
||||
return true;
|
||||
}
|
||||
if (log)
|
||||
log->Printf("Running to ObjC method implementation: 0x%llx", target_addr);
|
||||
|
||||
|
|
|
@ -83,23 +83,6 @@ ThreadPlanStepInRange::ShouldStop (Event *event_ptr)
|
|||
if (InRange())
|
||||
return false;
|
||||
|
||||
// If we're in an older frame then we should stop.
|
||||
if (FrameIsOlder())
|
||||
return true;
|
||||
|
||||
// See if we are in a place we should step through (i.e. a trampoline of some sort):
|
||||
// One tricky bit here is that some stubs don't push a frame, so we have to check
|
||||
// both the case of a frame that is younger, or the same as this frame.
|
||||
// However, if the frame is the same, and we are still in the symbol we started
|
||||
// in, the we don't need to do this. This first check isn't strictly necessary,
|
||||
// but it is more efficient.
|
||||
|
||||
if (!FrameIsYounger() && InSymbol())
|
||||
{
|
||||
SetPlanComplete();
|
||||
return true;
|
||||
}
|
||||
|
||||
ThreadPlan* new_plan = NULL;
|
||||
|
||||
// Stepping through should be done stopping other threads in general, since we're setting a breakpoint and
|
||||
|
@ -111,7 +94,39 @@ ThreadPlanStepInRange::ShouldStop (Event *event_ptr)
|
|||
else
|
||||
stop_others = false;
|
||||
|
||||
new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others);
|
||||
if (FrameIsOlder())
|
||||
{
|
||||
// If we're in an older frame then we should stop.
|
||||
//
|
||||
// A caveat to this is if we think the frame is older but we're actually in a trampoline.
|
||||
// I'm going to make the assumption that you wouldn't RETURN to a trampoline. So if we are
|
||||
// in a trampoline we think the frame is older because the trampoline confused the backtracer.
|
||||
new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others);
|
||||
if (new_plan == NULL)
|
||||
return true;
|
||||
else if (log)
|
||||
{
|
||||
log->Printf("Thought I stepped out, but in fact arrived at a trampoline.");
|
||||
}
|
||||
|
||||
}
|
||||
else if (!FrameIsYounger() && InSymbol())
|
||||
{
|
||||
// If we are not in a place we should step through, we're done.
|
||||
// One tricky bit here is that some stubs don't push a frame, so we have to check
|
||||
// both the case of a frame that is younger, or the same as this frame.
|
||||
// However, if the frame is the same, and we are still in the symbol we started
|
||||
// in, the we don't need to do this. This first check isn't strictly necessary,
|
||||
// but it is more efficient.
|
||||
|
||||
SetPlanComplete();
|
||||
return true;
|
||||
}
|
||||
|
||||
// We may have set the plan up above in the FrameIsOlder section:
|
||||
|
||||
if (new_plan == NULL)
|
||||
new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others);
|
||||
|
||||
if (log)
|
||||
{
|
||||
|
|
|
@ -88,7 +88,19 @@ ThreadPlanStepOverRange::ShouldStop (Event *event_ptr)
|
|||
ThreadPlan* new_plan = NULL;
|
||||
|
||||
if (FrameIsOlder())
|
||||
return true;
|
||||
{
|
||||
// If we're in an older frame then we should stop.
|
||||
//
|
||||
// A caveat to this is if we think the frame is older but we're actually in a trampoline.
|
||||
// I'm going to make the assumption that you wouldn't RETURN to a trampoline. So if we are
|
||||
// in a trampoline we think the frame is older because the trampoline confused the backtracer.
|
||||
// As below, we step through first, and then try to figure out how to get back out again.
|
||||
|
||||
new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others);
|
||||
|
||||
if (new_plan != NULL && log)
|
||||
log->Printf("Thought I stepped out, but in fact arrived at a trampoline.");
|
||||
}
|
||||
else if (FrameIsYounger())
|
||||
{
|
||||
new_plan = m_thread.QueueThreadPlanForStepOut (false, NULL, true, stop_others, lldb::eVoteNo, lldb::eVoteNoOpinion);
|
||||
|
|
Loading…
Reference in New Issue