MachThreadContext_i386.cpp   [plain text]


//===-- 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__)