blob: 5141e4b67eb7b91150baaf5faffd8e370a5b6b95 [file] [log] [blame]
//===-- MachThreadContext_i386.cpp ------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#if defined (__i386__) || defined (__x86_64__)
#include "MachThreadContext_i386.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/Symbol.h"
#include "ProcessMacOSX.h"
#include "ThreadMacOSX.h"
using namespace lldb;
using namespace lldb_private;
MachThreadContext_i386::MachThreadContext_i386 (ThreadMacOSX &thread) :
MachThreadContext (thread),
m_flags_reg(LLDB_INVALID_REGNUM)
{
}
MachThreadContext_i386::~MachThreadContext_i386()
{
}
MachThreadContext*
MachThreadContext_i386::Create (const ArchSpec &arch_spec, ThreadMacOSX &thread)
{
return new MachThreadContext_i386(thread);
}
// Class init function
void
MachThreadContext_i386::Initialize()
{
llvm::Triple triple;
triple.setArch (llvm::Triple::x86);
triple.setVendor (llvm::Triple::Apple);
triple.setOS (llvm::Triple::Darwin);
ArchSpec arch_spec (triple);
ProcessMacOSX::AddArchCreateCallback(arch_spec, MachThreadContext_i386::Create);
}
// Instance init function
void
MachThreadContext_i386::InitializeInstance()
{
RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
assert (reg_ctx != NULL);
m_flags_reg = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
}
void
MachThreadContext_i386::ThreadWillResume()
{
m_thread.GetRegisterContext()->HardwareSingleStep (m_thread.GetState() == eStateStepping);
}
bool
MachThreadContext_i386::ShouldStop()
{
return true;
}
void
MachThreadContext_i386::RefreshStateAfterStop()
{
m_thread.GetRegisterContext()->HardwareSingleStep (false);
}
bool
MachThreadContext_i386::NotifyException (MachException::Data& exc)
{
switch (exc.exc_type)
{
case EXC_BAD_ACCESS:
break;
case EXC_BAD_INSTRUCTION:
break;
case EXC_ARITHMETIC:
break;
case EXC_EMULATION:
break;
case EXC_SOFTWARE:
break;
case EXC_BREAKPOINT:
if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2)
{
RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
assert (reg_ctx);
lldb::addr_t pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS);
if (pc != LLDB_INVALID_ADDRESS && pc > 0)
{
pc -= 1;
reg_ctx->SetPC(pc);
}
return true;
}
break;
case EXC_SYSCALL:
break;
case EXC_MACH_SYSCALL:
break;
case EXC_RPC_ALERT:
break;
}
return false;
}
// Set the single step bit in the processor status register.
//kern_return_t
//MachThreadContext_i386::EnableHardwareSingleStep (bool enable)
//{
// RegisterContext *reg_ctx = m_thread.GetRegisterContext();
// assert (reg_ctx);
// Scalar rflags_scalar;
//
// if (reg_ctx->ReadRegisterValue (m_flags_reg, rflags_scalar))
// {
// Flags rflags(rflags_scalar.UInt());
// const uint32_t trace_bit = 0x100u;
// if (enable)
// {
// // If the trace bit is already cleared, there is nothing to do
// if (rflags.IsSet (trace_bit))
// return KERN_SUCCESS;
// else
// rflags.Set (trace_bit);
// }
// else
// {
// // If the trace bit is already cleared, there is nothing to do
// if (rflags.IsClear (trace_bit))
// return KERN_SUCCESS;
// else
// rflags.Clear(trace_bit);
// }
//
// rflags_scalar = rflags.Get();
// // If the code makes it here we have changes to the GPRs which
// // we need to write back out, so lets do that.
// if (reg_ctx->WriteRegisterValue(m_flags_reg, rflags_scalar))
// return KERN_SUCCESS;
// }
// // Return the error code for reading the GPR registers back
// return KERN_INVALID_ARGUMENT;
//}
RegisterContextSP
MachThreadContext_i386::CreateRegisterContext (StackFrame *frame) const
{
lldb::RegisterContextSP reg_ctx_sp (new RegisterContextMach_i386(m_thread, frame->GetConcreteFrameIndex()));
return reg_ctx_sp;
}
size_t
MachThreadContext_i386::GetStackFrameData(StackFrame *first_frame, std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs)
{
fp_pc_pairs.clear();
std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair;
struct Frame_i386
{
uint32_t fp;
uint32_t pc;
};
RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
assert (reg_ctx);
Frame_i386 frame = { reg_ctx->GetFP(0), reg_ctx->GetPC(LLDB_INVALID_ADDRESS) };
fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc));
const size_t k_frame_size = sizeof(frame);
Error error;
while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0))
{
// Read both the FP and PC (8 bytes)
if (m_thread.GetProcess().ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size)
break;
if (frame.pc != 0)
fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc));
}
if (!fp_pc_pairs.empty())
{
lldb::addr_t first_frame_pc = fp_pc_pairs.front().second;
if (first_frame_pc != LLDB_INVALID_ADDRESS)
{
const uint32_t resolve_scope = eSymbolContextModule |
eSymbolContextCompUnit |
eSymbolContextFunction |
eSymbolContextSymbol;
SymbolContext first_frame_sc(first_frame->GetSymbolContext(resolve_scope));
const AddressRange *addr_range_ptr = NULL;
if (first_frame_sc.function)
addr_range_ptr = &first_frame_sc.function->GetAddressRange();
else if (first_frame_sc.symbol)
addr_range_ptr = first_frame_sc.symbol->GetAddressRangePtr();
if (addr_range_ptr)
{
if (first_frame->GetFrameCodeAddress() == addr_range_ptr->GetBaseAddress())
{
// We are at the first instruction, so we can recover the
// previous PC by dereferencing the SP
lldb::addr_t first_frame_sp = reg_ctx->GetSP(0);
// Read the real second frame return address into frame.pc
if (m_thread.GetProcess().ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc))
{
// Construct a correct second frame (we already read the pc for it above
frame.fp = fp_pc_pairs.front().first;
// Insert the frame
fp_pc_pairs.insert(fp_pc_pairs.begin()+1, std::make_pair(frame.fp, frame.pc));
// Correct the fp in the first frame to use the SP
fp_pc_pairs.front().first = first_frame_sp;
}
}
}
}
}
return fp_pc_pairs.size();
}
#endif // #if defined (__i386__)