RegisterContextLinux_i386.cpp   [plain text]


//===-- RegisterContextLinux_i386.cpp ---------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/Core/DataExtractor.h"
#include "lldb/Target/Thread.h"
#include "lldb/Host/Endian.h"

#include "ProcessLinux.h"
#include "ProcessMonitor.h"
#include "RegisterContextLinux_i386.h"

using namespace lldb_private;
using namespace lldb;

enum
{
    k_first_gpr,
    gpr_eax = k_first_gpr,
    gpr_ebx,
    gpr_ecx,
    gpr_edx,
    gpr_edi,
    gpr_esi,
    gpr_ebp,
    gpr_esp,
    gpr_ss,
    gpr_eflags,
    gpr_orig_ax,
    gpr_eip,
    gpr_cs,
    gpr_ds,
    gpr_es,
    gpr_fs,
    gpr_gs,
    k_last_gpr = gpr_gs,

    k_first_fpr,
    fpu_fcw = k_first_fpr,
    fpu_fsw,
    fpu_ftw,
    fpu_fop,
    fpu_ip,
    fpu_cs,
    fpu_foo,
    fpu_fos,
    fpu_mxcsr,
    fpu_stmm0,
    fpu_stmm1,
    fpu_stmm2,
    fpu_stmm3,
    fpu_stmm4,
    fpu_stmm5,
    fpu_stmm6,
    fpu_stmm7,
    fpu_xmm0,
    fpu_xmm1,
    fpu_xmm2,
    fpu_xmm3,
    fpu_xmm4,
    fpu_xmm5,
    fpu_xmm6,
    fpu_xmm7,
    k_last_fpr = fpu_xmm7,

    k_num_registers,
    k_num_gpr_registers = k_last_gpr - k_first_gpr + 1,
    k_num_fpu_registers = k_last_fpr - k_first_fpr + 1
};

// Number of register sets provided by this context.
enum
{
    k_num_register_sets = 2
};

enum
{
    gcc_eax = 0,
    gcc_ecx,
    gcc_edx,
    gcc_ebx,
    gcc_ebp,
    gcc_esp,
    gcc_esi,
    gcc_edi,
    gcc_eip,
    gcc_eflags
};

enum
{
    dwarf_eax = 0,
    dwarf_ecx,
    dwarf_edx,
    dwarf_ebx,
    dwarf_esp,
    dwarf_ebp,
    dwarf_esi,
    dwarf_edi,
    dwarf_eip,
    dwarf_eflags,
    dwarf_stmm0 = 11,
    dwarf_stmm1,
    dwarf_stmm2,
    dwarf_stmm3,
    dwarf_stmm4,
    dwarf_stmm5,
    dwarf_stmm6,
    dwarf_stmm7,
    dwarf_xmm0 = 21,
    dwarf_xmm1,
    dwarf_xmm2,
    dwarf_xmm3,
    dwarf_xmm4,
    dwarf_xmm5,
    dwarf_xmm6,
    dwarf_xmm7
};

enum
{
    gdb_eax        =  0,
    gdb_ecx        =  1,
    gdb_edx        =  2,
    gdb_ebx        =  3,
    gdb_esp        =  4,
    gdb_ebp        =  5,
    gdb_esi        =  6,
    gdb_edi        =  7,
    gdb_eip        =  8,
    gdb_eflags     =  9,
    gdb_cs         = 10,
    gdb_ss         = 11,
    gdb_ds         = 12,
    gdb_es         = 13,
    gdb_fs         = 14,
    gdb_gs         = 15,
    gdb_stmm0      = 16,
    gdb_stmm1      = 17,
    gdb_stmm2      = 18,
    gdb_stmm3      = 19,
    gdb_stmm4      = 20,
    gdb_stmm5      = 21,
    gdb_stmm6      = 22,
    gdb_stmm7      = 23,
    gdb_fcw        = 24,
    gdb_fsw        = 25,
    gdb_ftw        = 26,
    gdb_fpu_cs     = 27,
    gdb_ip         = 28,
    gdb_fpu_ds     = 29,
    gdb_dp         = 30,
    gdb_fop        = 31,
    gdb_xmm0       = 32,
    gdb_xmm1       = 33,
    gdb_xmm2       = 34,
    gdb_xmm3       = 35,
    gdb_xmm4       = 36,
    gdb_xmm5       = 37,
    gdb_xmm6       = 38,
    gdb_xmm7       = 39,
    gdb_mxcsr      = 40,
    gdb_mm0        = 41,
    gdb_mm1        = 42,
    gdb_mm2        = 43,
    gdb_mm3        = 44,
    gdb_mm4        = 45,
    gdb_mm5        = 46,
    gdb_mm6        = 47,
    gdb_mm7        = 48
};

static const
uint32_t g_gpr_regnums[k_num_gpr_registers] =
{
    gpr_eax,
    gpr_ebx,
    gpr_ecx,
    gpr_edx,
    gpr_edi,
    gpr_esi,
    gpr_ebp,
    gpr_esp,
    gpr_ss,
    gpr_eflags,
    gpr_orig_ax,
    gpr_eip,
    gpr_cs,
    gpr_ds,
    gpr_es,
    gpr_fs,
    gpr_gs,
};

static const uint32_t
g_fpu_regnums[k_num_fpu_registers] =
{
    fpu_fcw,
    fpu_fsw,
    fpu_ftw,
    fpu_fop,
    fpu_ip,
    fpu_cs,
    fpu_foo,
    fpu_fos,
    fpu_mxcsr,
    fpu_stmm0,
    fpu_stmm1,
    fpu_stmm2,
    fpu_stmm3,
    fpu_stmm4,
    fpu_stmm5,
    fpu_stmm6,
    fpu_stmm7,
    fpu_xmm0,
    fpu_xmm1,
    fpu_xmm2,
    fpu_xmm3,
    fpu_xmm4,
    fpu_xmm5,
    fpu_xmm6,
    fpu_xmm7,
};

static const RegisterSet
g_reg_sets[k_num_register_sets] =
{
    { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums },
    { "Floating Point Registers",  "fpu", k_num_fpu_registers, g_fpu_regnums }
};

// Computes the offset of the given GPR in the user data area.
#define GPR_OFFSET(regname) \
    (offsetof(RegisterContextLinux_i386::UserArea, regs) + \
     offsetof(RegisterContextLinux_i386::GPR, regname))

// Computes the offset of the given FPR in the user data area.
#define FPR_OFFSET(regname) \
    (offsetof(RegisterContextLinux_i386::UserArea, i387) + \
     offsetof(RegisterContextLinux_i386::FPU, regname))

// Number of bytes needed to represent a GPR.
#define GPR_SIZE(reg) sizeof(((RegisterContextLinux_i386::GPR*)NULL)->reg)

// Number of bytes needed to represent a FPR.
#define FPR_SIZE(reg) sizeof(((RegisterContextLinux_i386::FPU*)NULL)->reg)

// Number of bytes needed to represent the i'th FP register.
#define FP_SIZE sizeof(((RegisterContextLinux_i386::MMSReg*)NULL)->bytes)

// Number of bytes needed to represent an XMM register.
#define XMM_SIZE sizeof(RegisterContextLinux_i386::XMMReg)

#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4)        \
    { #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), eEncodingUint, \
      eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg } }

#define DEFINE_FPR(reg, kind1, kind2, kind3, kind4)              \
    { #reg, NULL, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, \
      eFormatHex, { kind1, kind2, kind3, kind4, fpu_##reg } }

#define DEFINE_FP(reg, i)                                          \
    { #reg#i, NULL, FP_SIZE, FPR_OFFSET(reg[i]), eEncodingVector,  \
      eFormatVectorOfUInt8,                                        \
      { dwarf_##reg##i, dwarf_##reg##i,                            \
        LLDB_INVALID_REGNUM, gdb_##reg##i, fpu_##reg##i } }

#define DEFINE_XMM(reg, i)                                         \
    { #reg#i, NULL, XMM_SIZE, FPR_OFFSET(reg[i]), eEncodingVector, \
      eFormatVectorOfUInt8,                                        \
      { dwarf_##reg##i, dwarf_##reg##i,                            \
        LLDB_INVALID_REGNUM, gdb_##reg##i, fpu_##reg##i } }

static RegisterInfo
g_register_infos[k_num_registers] =
{
    // General purpose registers.
    DEFINE_GPR(eax,    NULL,    gcc_eax,    dwarf_eax,    LLDB_INVALID_REGNUM,    gdb_eax),
    DEFINE_GPR(ebx,    NULL,    gcc_ebx,    dwarf_ebx,    LLDB_INVALID_REGNUM,    gdb_ebx),
    DEFINE_GPR(ecx,    NULL,    gcc_ecx,    dwarf_ecx,    LLDB_INVALID_REGNUM,    gdb_ecx),
    DEFINE_GPR(edx,    NULL,    gcc_edx,    dwarf_edx,    LLDB_INVALID_REGNUM,    gdb_edx),
    DEFINE_GPR(edi,    NULL,    gcc_edi,    dwarf_edi,    LLDB_INVALID_REGNUM,    gdb_edi),
    DEFINE_GPR(esi,    NULL,    gcc_esi,    dwarf_esi,    LLDB_INVALID_REGNUM,    gdb_esi),
    DEFINE_GPR(ebp,    "fp",    gcc_ebp,    dwarf_ebp,    LLDB_INVALID_REGNUM,    gdb_ebp),
    DEFINE_GPR(esp,    "sp",    gcc_esp,    dwarf_esp,    LLDB_INVALID_REGNUM,    gdb_esp),
    DEFINE_GPR(ss,     NULL,    LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,    gdb_ss),
    DEFINE_GPR(eflags, "flags", gcc_eflags, dwarf_eflags, LLDB_INVALID_REGNUM,    gdb_eflags),
    DEFINE_GPR(eip,    "pc",    gcc_eip,    dwarf_eip,    LLDB_INVALID_REGNUM,    gdb_eip),
    DEFINE_GPR(cs,     NULL,    LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,    gdb_cs),
    DEFINE_GPR(ds,     NULL,    LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,    gdb_ds),
    DEFINE_GPR(es,     NULL,    LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,    gdb_es),
    DEFINE_GPR(fs,     NULL,    LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,    gdb_fs),
    DEFINE_GPR(gs,     NULL,    LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,     LLDB_INVALID_REGNUM,    gdb_gs),

    // Floating point registers.
    DEFINE_FPR(fcw,       LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fcw),
    DEFINE_FPR(fsw,       LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fsw),
    DEFINE_FPR(ftw,       LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ftw),
    DEFINE_FPR(fop,       LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fop),
    DEFINE_FPR(ip,        LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ip),
    DEFINE_FPR(cs,        LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_cs),
    DEFINE_FPR(foo,       LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_dp),
    DEFINE_FPR(fos,       LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ds),
    DEFINE_FPR(mxcsr,     LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_mxcsr),

    DEFINE_FP(stmm, 0),
    DEFINE_FP(stmm, 1),
    DEFINE_FP(stmm, 2),
    DEFINE_FP(stmm, 3),
    DEFINE_FP(stmm, 4),
    DEFINE_FP(stmm, 5),
    DEFINE_FP(stmm, 6),
    DEFINE_FP(stmm, 7),

    // XMM registers
    DEFINE_XMM(xmm, 0),
    DEFINE_XMM(xmm, 1),
    DEFINE_XMM(xmm, 2),
    DEFINE_XMM(xmm, 3),
    DEFINE_XMM(xmm, 4),
    DEFINE_XMM(xmm, 5),
    DEFINE_XMM(xmm, 6),
    DEFINE_XMM(xmm, 7),

};

static unsigned GetRegOffset(unsigned reg)
{
    assert(reg < k_num_registers && "Invalid register number.");
    return g_register_infos[reg].byte_offset;
}

static unsigned GetRegSize(unsigned reg)
{
    assert(reg < k_num_registers && "Invalid register number.");
    return g_register_infos[reg].byte_size;
}

static bool IsGPR(unsigned reg)
{
    return reg <= k_last_gpr;   // GPR's come first.
}

static bool IsFPR(unsigned reg)
{
    return (k_first_fpr <= reg && reg <= k_last_fpr);
}


RegisterContextLinux_i386::RegisterContextLinux_i386(Thread &thread,
                                                     uint32_t concrete_frame_idx)
    : RegisterContextLinux(thread, concrete_frame_idx)
{
}

RegisterContextLinux_i386::~RegisterContextLinux_i386()
{
}

ProcessMonitor &
RegisterContextLinux_i386::GetMonitor()
{
    ProcessLinux *process = static_cast<ProcessLinux*>(CalculateProcess());
    return process->GetMonitor();
}

void
RegisterContextLinux_i386::Invalidate()
{
}

void
RegisterContextLinux_i386::InvalidateAllRegisters()
{
}

size_t
RegisterContextLinux_i386::GetRegisterCount()
{
    return k_num_registers;
}

const RegisterInfo *
RegisterContextLinux_i386::GetRegisterInfoAtIndex(uint32_t reg)
{
    if (reg < k_num_registers)
        return &g_register_infos[reg];
    else
        return NULL;
}

size_t
RegisterContextLinux_i386::GetRegisterSetCount()
{
    return k_num_register_sets;
}

const RegisterSet *
RegisterContextLinux_i386::GetRegisterSet(uint32_t set)
{
    if (set < k_num_register_sets)
        return &g_reg_sets[set];
    else
        return NULL;
}

bool
RegisterContextLinux_i386::ReadRegister(const RegisterInfo *reg_info,
                                        RegisterValue &value)
{
    const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
    ProcessMonitor &monitor = GetMonitor();
    return monitor.ReadRegisterValue(GetRegOffset(reg), value);
}

bool
RegisterContextLinux_i386::ReadAllRegisterValues(DataBufferSP &data_sp)
{
    return false;
}

bool RegisterContextLinux_i386::WriteRegister(const RegisterInfo *reg_info,
                                              const RegisterValue &value)
{
    const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
    ProcessMonitor &monitor = GetMonitor();
    return monitor.WriteRegisterValue(GetRegOffset(reg), value);
}

bool
RegisterContextLinux_i386::WriteAllRegisterValues(const DataBufferSP &data)
{
    return false;
}

bool
RegisterContextLinux_i386::UpdateAfterBreakpoint()
{
    // PC points one byte past the int3 responsible for the breakpoint.
    lldb::addr_t pc;

    if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
        return false;

    SetPC(pc - 1);
    return true;
}

uint32_t
RegisterContextLinux_i386::ConvertRegisterKindToRegisterNumber(uint32_t kind,
                                                               uint32_t num)
{
    if (kind == eRegisterKindGeneric)
    {
        switch (num)
        {
        case LLDB_REGNUM_GENERIC_PC:    return gpr_eip;
        case LLDB_REGNUM_GENERIC_SP:    return gpr_esp;
        case LLDB_REGNUM_GENERIC_FP:    return gpr_ebp;
        case LLDB_REGNUM_GENERIC_FLAGS: return gpr_eflags;
        case LLDB_REGNUM_GENERIC_RA:
        default:
            return LLDB_INVALID_REGNUM;
        }
    }

    if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF)
    {
        switch (num)
        {
        case dwarf_eax:  return gpr_eax;
        case dwarf_edx:  return gpr_edx;
        case dwarf_ecx:  return gpr_ecx;
        case dwarf_ebx:  return gpr_ebx;
        case dwarf_esi:  return gpr_esi;
        case dwarf_edi:  return gpr_edi;
        case dwarf_ebp:  return gpr_ebp;
        case dwarf_esp:  return gpr_esp;
        case dwarf_eip:  return gpr_eip;
        case dwarf_xmm0: return fpu_xmm0;
        case dwarf_xmm1: return fpu_xmm1;
        case dwarf_xmm2: return fpu_xmm2;
        case dwarf_xmm3: return fpu_xmm3;
        case dwarf_xmm4: return fpu_xmm4;
        case dwarf_xmm5: return fpu_xmm5;
        case dwarf_xmm6: return fpu_xmm6;
        case dwarf_xmm7: return fpu_xmm7;
        case dwarf_stmm0: return fpu_stmm0;
        case dwarf_stmm1: return fpu_stmm1;
        case dwarf_stmm2: return fpu_stmm2;
        case dwarf_stmm3: return fpu_stmm3;
        case dwarf_stmm4: return fpu_stmm4;
        case dwarf_stmm5: return fpu_stmm5;
        case dwarf_stmm6: return fpu_stmm6;
        case dwarf_stmm7: return fpu_stmm7;
        default:
            return LLDB_INVALID_REGNUM;
        }
    }

    if (kind == eRegisterKindGDB)
    {
        switch (num)
        {
        case gdb_eax     : return gpr_eax;
        case gdb_ebx     : return gpr_ebx;
        case gdb_ecx     : return gpr_ecx;
        case gdb_edx     : return gpr_edx;
        case gdb_esi     : return gpr_esi;
        case gdb_edi     : return gpr_edi;
        case gdb_ebp     : return gpr_ebp;
        case gdb_esp     : return gpr_esp;
        case gdb_eip     : return gpr_eip;
        case gdb_eflags  : return gpr_eflags;
        case gdb_cs      : return gpr_cs;
        case gdb_ss      : return gpr_ss;
        case gdb_ds      : return gpr_ds;
        case gdb_es      : return gpr_es;
        case gdb_fs      : return gpr_fs;
        case gdb_gs      : return gpr_gs;
        case gdb_stmm0   : return fpu_stmm0;
        case gdb_stmm1   : return fpu_stmm1;
        case gdb_stmm2   : return fpu_stmm2;
        case gdb_stmm3   : return fpu_stmm3;
        case gdb_stmm4   : return fpu_stmm4;
        case gdb_stmm5   : return fpu_stmm5;
        case gdb_stmm6   : return fpu_stmm6;
        case gdb_stmm7   : return fpu_stmm7;
        case gdb_fcw     : return fpu_fcw;
        case gdb_fsw     : return fpu_fsw;
        case gdb_ftw     : return fpu_ftw;
        case gdb_fpu_cs  : return fpu_cs;
        case gdb_ip      : return fpu_ip;
        case gdb_fpu_ds  : return fpu_fos;
        case gdb_dp      : return fpu_foo;
        case gdb_fop     : return fpu_fop;
        case gdb_xmm0    : return fpu_xmm0;
        case gdb_xmm1    : return fpu_xmm1;
        case gdb_xmm2    : return fpu_xmm2;
        case gdb_xmm3    : return fpu_xmm3;
        case gdb_xmm4    : return fpu_xmm4;
        case gdb_xmm5    : return fpu_xmm5;
        case gdb_xmm6    : return fpu_xmm6;
        case gdb_xmm7    : return fpu_xmm7;
        case gdb_mxcsr   : return fpu_mxcsr;
        default:
            return LLDB_INVALID_REGNUM;
        }
    }
    else if (kind == eRegisterKindLLDB)
    {
        return num;
    }

    return LLDB_INVALID_REGNUM;
}

bool
RegisterContextLinux_i386::HardwareSingleStep(bool enable)
{
    enum { TRACE_BIT = 0x100 };
    uint64_t eflags;

    if ((eflags = ReadRegisterAsUnsigned(gpr_eflags, -1UL)) == -1UL)
        return false;

    if (enable)
    {
        if (eflags & TRACE_BIT)
            return true;

        eflags |= TRACE_BIT;
    }
    else
    {
        if (!(eflags & TRACE_BIT))
            return false;

        eflags &= ~TRACE_BIT;
    }

    return WriteRegisterFromUnsigned(gpr_eflags, eflags);
}

bool
RegisterContextLinux_i386::ReadGPR()
{
     ProcessMonitor &monitor = GetMonitor();
     return monitor.ReadGPR(&user.regs);
}

bool
RegisterContextLinux_i386::ReadFPR()
{
    ProcessMonitor &monitor = GetMonitor();
    return monitor.ReadFPR(&user.i387);
}