#include "defs.h"
#include "arch-utils.h"
#include "command.h"
#include "dummy-frame.h"
#include "dwarf2-frame.h"
#include "doublest.h"
#include "floatformat.h"
#include "frame.h"
#include "frame-base.h"
#include "frame-unwind.h"
#include "inferior.h"
#include "gdbcmd.h"
#include "gdbcore.h"
#include "objfiles.h"
#include "osabi.h"
#include "regcache.h"
#include "reggroups.h"
#include "regset.h"
#include "symfile.h"
#include "symtab.h"
#include "target.h"
#include "value.h"
#include "gdb_assert.h"
#include "reggroups.h"
#include "dummy-frame.h"
#include "osabi.h"
#include "bfd.h"
#include "elf-bfd.h"
#include "dis-asm.h"
#include "gdb_assert.h"
#include "gdb_string.h"
#include "i386-tdep.h"
#include "i387-tdep.h"
#include "x86-shared-tdep.h"
#include "macosx-tdep.h"
static char *i386_register_names[] =
{
"eax", "ecx", "edx", "ebx",
"esp", "ebp", "esi", "edi",
"eip", "eflags", "cs", "ss",
"ds", "es", "fs", "gs",
"st0", "st1", "st2", "st3",
"st4", "st5", "st6", "st7",
"fctrl", "fstat", "ftag", "fiseg",
"fioff", "foseg", "fooff", "fop",
"xmm0", "xmm1", "xmm2", "xmm3",
"xmm4", "xmm5", "xmm6", "xmm7",
"mxcsr"
};
static const int i386_num_register_names = ARRAY_SIZE (i386_register_names);
static char *i386_mmx_names[] =
{
"mm0", "mm1", "mm2", "mm3",
"mm4", "mm5", "mm6", "mm7"
};
static const int i386_num_mmx_regs = ARRAY_SIZE (i386_mmx_names);
struct type *builtin_type_vec128i_big = NULL;
static int
i386_mmx_regnum_p (struct gdbarch *gdbarch, int regnum)
{
int mm0_regnum = gdbarch_tdep (gdbarch)->mm0_regnum;
if (mm0_regnum < 0)
return 0;
return (regnum >= mm0_regnum && regnum < mm0_regnum + i386_num_mmx_regs);
}
static int
i386_sse_regnum_p (struct gdbarch *gdbarch, int regnum)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
#define I387_ST0_REGNUM tdep->st0_regnum
#define I387_NUM_XMM_REGS tdep->num_xmm_regs
if (I387_NUM_XMM_REGS == 0)
return 0;
return (I387_XMM0_REGNUM <= regnum && regnum < I387_MXCSR_REGNUM);
#undef I387_ST0_REGNUM
#undef I387_NUM_XMM_REGS
}
static int
i386_mxcsr_regnum_p (struct gdbarch *gdbarch, int regnum)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
#define I387_ST0_REGNUM tdep->st0_regnum
#define I387_NUM_XMM_REGS tdep->num_xmm_regs
if (I387_NUM_XMM_REGS == 0)
return 0;
return (regnum == I387_MXCSR_REGNUM);
#undef I387_ST0_REGNUM
#undef I387_NUM_XMM_REGS
}
#define I387_ST0_REGNUM (gdbarch_tdep (current_gdbarch)->st0_regnum)
#define I387_MM0_REGNUM (gdbarch_tdep (current_gdbarch)->mm0_regnum)
#define I387_NUM_XMM_REGS (gdbarch_tdep (current_gdbarch)->num_xmm_regs)
int
i386_fp_regnum_p (int regnum)
{
if (I387_ST0_REGNUM < 0)
return 0;
return (I387_ST0_REGNUM <= regnum && regnum < I387_FCTRL_REGNUM);
}
int
i386_fpc_regnum_p (int regnum)
{
if (I387_ST0_REGNUM < 0)
return 0;
return (I387_FCTRL_REGNUM <= regnum && regnum < I387_XMM0_REGNUM);
}
const char *
i386_register_name (int regnum)
{
if (i386_mmx_regnum_p (current_gdbarch, regnum))
return i386_mmx_names[regnum - I387_MM0_REGNUM];
if (regnum >= 0 && regnum < i386_num_register_names)
return i386_register_names[regnum];
return NULL;
}
static int
i386_dbx_reg_to_regnum (int reg)
{
if (reg >= 0 && reg <= 7)
{
if (reg == 4)
return 5;
else if (reg == 5)
return 4;
else return reg;
}
else if (reg >= 12 && reg <= 19)
{
return reg - 12 + I387_ST0_REGNUM;
}
else if (reg >= 21 && reg <= 28)
{
return reg - 21 + I387_XMM0_REGNUM;
}
else if (reg >= 29 && reg <= 36)
{
return reg - 29 + I387_MM0_REGNUM;
}
return NUM_REGS + NUM_PSEUDO_REGS;
}
static int
i386_svr4_reg_to_regnum (int reg)
{
if (reg >= 0 && reg <= 9)
{
return reg;
}
else if (reg >= 11 && reg <= 18)
{
return reg - 11 + I387_ST0_REGNUM;
}
else if (reg >= 21)
{
return i386_dbx_reg_to_regnum (reg);
}
return NUM_REGS + NUM_PSEUDO_REGS;
}
static int
i386_adjust_ehframe_regnum (struct gdbarch *gdbarch, int r, int ehframe_p)
{
if (ehframe_p == 0)
return r;
if (r == 5)
return 4;
if (r == 4)
return 5;
if (r >= 12 && r <= 19)
return r - 1;
return r;
}
#undef I387_ST0_REGNUM
#undef I387_MM0_REGNUM
#undef I387_NUM_XMM_REGS
static const char att_flavor[] = "att";
static const char intel_flavor[] = "intel";
static const char *valid_flavors[] =
{
att_flavor,
intel_flavor,
NULL
};
static const char *disassembly_flavor = att_flavor;
static const gdb_byte *
i386_breakpoint_from_pc (CORE_ADDR *pc, int *len)
{
static gdb_byte break_insn[] = { 0xcc };
*len = sizeof (break_insn);
return break_insn;
}
#define I386_NUM_SAVED_REGS I386_NUM_GREGS
int
i386_find_picbase_setup (CORE_ADDR pc, CORE_ADDR *picbase_addr,
enum i386_regnum *picbase_reg)
{
int limit = 32;
int skip = 0;
int found_call_insn = 0;
unsigned char op;
if (picbase_addr != NULL)
*picbase_addr = -1;
while (skip < limit)
{
int length = length_of_this_instruction (pc + skip);
if (length == 5)
{
uint32_t buf = read_memory_unsigned_integer (pc + skip, 1);
if (buf == 0xe8)
{
found_call_insn = 1;
break;
}
}
skip += length;
}
if (!found_call_insn)
return 0;
uint32_t rel32 = read_memory_unsigned_integer (pc + skip + 1, 4);
uint32_t offset_from = pc + skip + 5;
struct minimal_symbol *dest;
if (rel32 == 0x0)
{
op = read_memory_unsigned_integer (offset_from, 1);
if ((op & 0xf8) != 0x58)
return 0;
if (picbase_addr != NULL)
*picbase_addr = offset_from;
if (picbase_reg != NULL)
*picbase_reg = (enum i386_regnum) op & 0x7;
return 1;
}
dest = lookup_minimal_symbol_by_pc ((uint32_t) rel32 + offset_from);
if (dest == NULL || SYMBOL_LINKAGE_NAME (dest) == NULL)
return 0;
if (strcmp (SYMBOL_LINKAGE_NAME (dest), "__i686.get_pc_thunk.ax") == 0)
{
if (picbase_addr != NULL)
*picbase_addr = offset_from;
if (picbase_reg != NULL)
*picbase_reg = I386_EAX_REGNUM;
return 1;
}
if (strcmp (SYMBOL_LINKAGE_NAME (dest), "__i686.get_pc_thunk.bx") == 0)
{
if (picbase_addr != NULL)
*picbase_addr = offset_from;
if (picbase_reg != NULL)
*picbase_reg = I386_EBX_REGNUM;
return 1;
}
if (strcmp (SYMBOL_LINKAGE_NAME (dest), "__i686.get_pc_thunk.cx") == 0)
{
if (picbase_addr != NULL)
*picbase_addr = offset_from;
if (picbase_reg != NULL)
*picbase_reg = I386_ECX_REGNUM;
return 1;
}
if (strcmp (SYMBOL_LINKAGE_NAME (dest), "__i686.get_pc_thunk.dx") == 0)
{
if (picbase_addr != NULL)
*picbase_addr = offset_from;
if (picbase_reg != NULL)
*picbase_reg = I386_EDX_REGNUM;
return 1;
}
return 0;
}
static CORE_ADDR
i386_skip_prologue (CORE_ADDR start_pc)
{
struct x86_frame_cache cache;
CORE_ADDR pc;
CORE_ADDR endaddr;
x86_initialize_frame_cache (&cache, 4);
if (find_pc_partial_function_no_inlined (start_pc, NULL, NULL, &endaddr) == 0)
endaddr = start_pc + 512;
endaddr = refine_prologue_limit (start_pc, endaddr, 3);
pc = x86_analyze_prologue (start_pc, endaddr, &cache);
return pc;
}
static CORE_ADDR
i386_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
gdb_byte buf[8];
frame_unwind_register (next_frame, PC_REGNUM, buf);
return extract_typed_address (buf, builtin_type_void_func_ptr);
}
static const struct frame_unwind i386_frame_unwind =
{
NORMAL_FRAME,
x86_frame_this_id,
x86_frame_prev_register,
NULL,
NULL,
NULL
};
static const struct frame_unwind *
i386_frame_sniffer (struct frame_info *next_frame)
{
return &i386_frame_unwind;
}
static struct x86_frame_cache *
i386_sigtramp_frame_cache (struct frame_info *next_frame, void **this_cache)
{
struct x86_frame_cache *cache;
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
CORE_ADDR addr;
gdb_byte buf[4];
if (*this_cache)
return *this_cache;
cache = x86_alloc_frame_cache (4);
frame_unwind_register (next_frame, I386_ESP_REGNUM, buf);
cache->frame_base = extract_unsigned_integer (buf, 4);
addr = tdep->sigcontext_addr (next_frame);
if (tdep->sc_reg_offset)
{
int i;
gdb_assert (tdep->sc_num_regs <= I386_NUM_SAVED_REGS);
for (i = 0; i < tdep->sc_num_regs; i++)
if (tdep->sc_reg_offset[i] != -1)
cache->saved_regs[i] = addr + tdep->sc_reg_offset[i];
}
else
{
cache->saved_regs[I386_EIP_REGNUM] = addr + tdep->sc_pc_offset;
cache->saved_regs[I386_ESP_REGNUM] = addr + tdep->sc_sp_offset;
}
cache->saved_regs_are_absolute = 1;
cache->prologue_scan_status = full_scan_succeeded;
*this_cache = cache;
return cache;
}
static void
i386_sigtramp_frame_this_id (struct frame_info *next_frame, void **this_cache,
struct frame_id *this_id)
{
struct x86_frame_cache *cache =
i386_sigtramp_frame_cache (next_frame, this_cache);
(*this_id) = frame_id_build (cache->frame_base + 4, frame_pc_unwind (next_frame));
}
static void
i386_sigtramp_frame_prev_register (struct frame_info *next_frame,
void **this_cache,
int regnum, enum opt_state *optimizedp,
enum lval_type *lvalp, CORE_ADDR *addrp,
int *realnump, gdb_byte *valuep)
{
i386_sigtramp_frame_cache (next_frame, this_cache);
x86_frame_prev_register (next_frame, this_cache, regnum,
optimizedp, lvalp, addrp, realnump, valuep);
}
static const struct frame_unwind i386_sigtramp_frame_unwind =
{
SIGTRAMP_FRAME,
i386_sigtramp_frame_this_id,
i386_sigtramp_frame_prev_register,
NULL,
NULL,
NULL
};
static const struct frame_unwind *
i386_sigtramp_frame_sniffer (struct frame_info *next_frame)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (next_frame));
if (tdep->sigcontext_addr == NULL)
return NULL;
if (tdep->sigtramp_p != NULL)
{
if (tdep->sigtramp_p (next_frame))
return &i386_sigtramp_frame_unwind;
}
if (tdep->sigtramp_start != 0)
{
CORE_ADDR pc = frame_pc_unwind (next_frame);
gdb_assert (tdep->sigtramp_end != 0);
if (pc >= tdep->sigtramp_start && pc < tdep->sigtramp_end)
return &i386_sigtramp_frame_unwind;
}
return NULL;
}
static CORE_ADDR
i386_frame_base_address (struct frame_info *next_frame, void **this_cache)
{
struct x86_frame_cache *cache = x86_frame_cache (next_frame, this_cache, 4);
x86_finalize_saved_reg_locations (next_frame, cache);
return cache->frame_base - 4;
}
static const struct frame_base i386_frame_base =
{
&i386_frame_unwind,
i386_frame_base_address,
i386_frame_base_address,
i386_frame_base_address
};
static struct frame_id
i386_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
gdb_byte buf[4];
CORE_ADDR fp;
frame_unwind_register (next_frame, I386_EBP_REGNUM, buf);
fp = extract_unsigned_integer (buf, 4);
return frame_id_build (fp + 8, frame_pc_unwind (next_frame));
}
static int
i386_get_longjmp_target (CORE_ADDR *pc)
{
gdb_byte buf[8];
CORE_ADDR sp, jb_addr;
int jb_pc_offset = gdbarch_tdep (current_gdbarch)->jb_pc_offset;
int len = TYPE_LENGTH (builtin_type_void_func_ptr);
if (jb_pc_offset == -1)
return 0;
regcache_cooked_read (current_regcache, SP_REGNUM, buf);
sp = extract_typed_address (buf, builtin_type_void_data_ptr);
if (target_read_memory (sp + len, buf, len))
return 0;
jb_addr = extract_typed_address (buf, builtin_type_void_data_ptr);
if (target_read_memory (jb_addr + jb_pc_offset, buf, len))
return 0;
*pc = extract_typed_address (buf, builtin_type_void_func_ptr);
return 1;
}
static CORE_ADDR
i386_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
struct regcache *regcache, CORE_ADDR bp_addr, int nargs,
struct value **args, CORE_ADDR sp, int struct_return,
CORE_ADDR struct_addr)
{
gdb_byte buf[4];
int i;
int argument_bytes = 0;
int alignment_pad_bytes;
for (i = nargs - 1; i >= 0; i--)
{
int len = TYPE_LENGTH (value_enclosing_type (args[i]));
argument_bytes += (len + 3) & ~3;
}
if (struct_return)
argument_bytes += 4;
alignment_pad_bytes = (sp - argument_bytes) - ((sp - argument_bytes) & ~15);
sp -= alignment_pad_bytes;
for (i = nargs - 1; i >= 0; i--)
{
int len = TYPE_LENGTH (value_enclosing_type (args[i]));
sp -= (len + 3) & ~3;
write_memory (sp, value_contents_all (args[i]), len);
}
if (struct_return)
{
sp -= 4;
store_unsigned_integer (buf, 4, struct_addr);
write_memory (sp, buf, 4);
}
sp -= 4;
store_unsigned_integer (buf, 4, bp_addr);
write_memory (sp, buf, 4);
store_unsigned_integer (buf, 4, sp);
regcache_cooked_write (regcache, I386_ESP_REGNUM, buf);
#if 0
regcache_cooked_write (regcache, I386_EBP_REGNUM, buf);
#endif
return read_register (I386_EBP_REGNUM) + 8;
}
#define LOW_RETURN_REGNUM I386_EAX_REGNUM
#define HIGH_RETURN_REGNUM I386_EDX_REGNUM
static void
i386_extract_return_value (struct gdbarch *gdbarch, struct type *type,
struct regcache *regcache, gdb_byte *valbuf)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
int len = TYPE_LENGTH (type);
gdb_byte buf[I386_MAX_REGISTER_SIZE];
if (TYPE_CODE (type) == TYPE_CODE_FLT)
{
if (tdep->st0_regnum < 0)
{
warning (_("Cannot find floating-point return value."));
memset (valbuf, 0, len);
return;
}
regcache_raw_read (regcache, I386_ST0_REGNUM, buf);
convert_typed_floating (buf, builtin_type_i387_ext, valbuf, type);
}
else
{
int low_size = register_size (current_gdbarch, LOW_RETURN_REGNUM);
int high_size = register_size (current_gdbarch, HIGH_RETURN_REGNUM);
if (len <= low_size)
{
regcache_raw_read (regcache, LOW_RETURN_REGNUM, buf);
memcpy (valbuf, buf, len);
}
else if (len <= (low_size + high_size))
{
regcache_raw_read (regcache, LOW_RETURN_REGNUM, buf);
memcpy (valbuf, buf, low_size);
regcache_raw_read (regcache, HIGH_RETURN_REGNUM, buf);
memcpy (valbuf + low_size, buf, len - low_size);
}
else
internal_error (__FILE__, __LINE__,
_("Cannot extract return value of %d bytes long."), len);
}
}
static void
i386_store_return_value (struct gdbarch *gdbarch, struct type *type,
struct regcache *regcache, const gdb_byte *valbuf)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
int len = TYPE_LENGTH (type);
#define I387_ST0_REGNUM I386_ST0_REGNUM
if (TYPE_CODE (type) == TYPE_CODE_FLT)
{
ULONGEST fstat;
gdb_byte buf[I386_MAX_REGISTER_SIZE];
if (tdep->st0_regnum < 0)
{
warning (_("Cannot set floating-point return value."));
return;
}
convert_typed_floating (valbuf, type, buf, builtin_type_i387_ext);
regcache_raw_write (regcache, I386_ST0_REGNUM, buf);
regcache_raw_read_unsigned (regcache, I387_FSTAT_REGNUM, &fstat);
fstat |= (7 << 11);
regcache_raw_write_unsigned (regcache, I387_FSTAT_REGNUM, fstat);
regcache_raw_write_unsigned (regcache, I387_FTAG_REGNUM, 0x3fff);
}
else
{
int low_size = register_size (current_gdbarch, LOW_RETURN_REGNUM);
int high_size = register_size (current_gdbarch, HIGH_RETURN_REGNUM);
if (len <= low_size)
regcache_raw_write_part (regcache, LOW_RETURN_REGNUM, 0, len, valbuf);
else if (len <= (low_size + high_size))
{
regcache_raw_write (regcache, LOW_RETURN_REGNUM, valbuf);
regcache_raw_write_part (regcache, HIGH_RETURN_REGNUM, 0,
len - low_size, valbuf + low_size);
}
else
internal_error (__FILE__, __LINE__,
_("Cannot store return value of %d bytes long."), len);
}
#undef I387_ST0_REGNUM
}
static const char default_struct_convention[] = "default";
static const char pcc_struct_convention[] = "pcc";
static const char reg_struct_convention[] = "reg";
static const char *valid_conventions[] =
{
default_struct_convention,
pcc_struct_convention,
reg_struct_convention,
NULL
};
static const char *struct_convention = default_struct_convention;
static int
i386_reg_struct_return_p (struct gdbarch *gdbarch, struct type *type)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
enum type_code code = TYPE_CODE (type);
int len = TYPE_LENGTH (type);
gdb_assert (code == TYPE_CODE_STRUCT || code == TYPE_CODE_UNION);
if (struct_convention == pcc_struct_convention
|| (struct_convention == default_struct_convention
&& tdep->struct_return == pcc_struct_return))
return 0;
if (code == TYPE_CODE_STRUCT && TYPE_NFIELDS (type) == 1)
{
type = check_typedef (TYPE_FIELD_TYPE (type, 0));
if (TYPE_CODE (type) == TYPE_CODE_FLT)
return (len == 4 || len == 8 || len == 12);
}
return (len == 1 || len == 2 || len == 4 || len == 8);
}
static enum return_value_convention
i386_return_value (struct gdbarch *gdbarch, struct type *type,
struct regcache *regcache, gdb_byte *readbuf,
const gdb_byte *writebuf)
{
enum type_code code = TYPE_CODE (type);
if ((code == TYPE_CODE_STRUCT || code == TYPE_CODE_UNION)
&& !i386_reg_struct_return_p (gdbarch, type))
{
if (readbuf)
{
ULONGEST addr;
regcache_raw_read_unsigned (regcache, I386_EAX_REGNUM, &addr);
read_memory (addr, readbuf, TYPE_LENGTH (type));
}
return RETURN_VALUE_ABI_RETURNS_ADDRESS;
}
if (code == TYPE_CODE_STRUCT && TYPE_NFIELDS (type) == 1)
{
type = check_typedef (TYPE_FIELD_TYPE (type, 0));
return i386_return_value (gdbarch, type, regcache, readbuf, writebuf);
}
if (readbuf)
i386_extract_return_value (gdbarch, type, regcache, readbuf);
if (writebuf)
i386_store_return_value (gdbarch, type, regcache, writebuf);
return RETURN_VALUE_REGISTER_CONVENTION;
}
static struct type *i386_mmx_type;
static struct type *
i386_build_mmx_type (void)
{
#if 0
union __gdb_builtin_type_vec64i
{
int64_t uint64;
int32_t v2_int32[2];
int16_t v4_int16[4];
int8_t v8_int8[8];
};
#endif
if (! i386_mmx_type)
{
struct type *t;
t = init_composite_type ("__gdb_builtin_type_vec64i", TYPE_CODE_UNION);
append_composite_type_field (t, "uint64", builtin_type_int64);
append_composite_type_field (t, "v2_int32", builtin_type_v2_int32);
append_composite_type_field (t, "v4_int16", builtin_type_v4_int16);
append_composite_type_field (t, "v8_int8", builtin_type_v8_int8);
TYPE_FLAGS (t) |= TYPE_FLAG_VECTOR;
TYPE_NAME (t) = "builtin_type_vec64i";
i386_mmx_type = t;
}
return i386_mmx_type;
}
static struct type *
i386_register_type (struct gdbarch *gdbarch, int regnum)
{
if (regnum == I386_EIP_REGNUM)
return builtin_type_void_func_ptr;
if (regnum == I386_EBP_REGNUM || regnum == I386_ESP_REGNUM)
return builtin_type_void_data_ptr;
if (i386_fp_regnum_p (regnum))
return builtin_type_i387_ext;
if (i386_sse_regnum_p (gdbarch, regnum))
return builtin_type_vec128i_big;
if (i386_mmx_regnum_p (gdbarch, regnum))
return i386_build_mmx_type ();
return builtin_type_int;
}
static int
i386_mmx_regnum_to_fp_regnum (struct regcache *regcache, int regnum)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (get_regcache_arch (regcache));
int mmxreg, fpreg;
ULONGEST fstat;
int tos;
#define I387_ST0_REGNUM tdep->st0_regnum
mmxreg = regnum - tdep->mm0_regnum;
regcache_raw_read_unsigned (regcache, I387_FSTAT_REGNUM, &fstat);
tos = (fstat >> 11) & 0x7;
fpreg = (mmxreg + tos) % 8;
return (I387_ST0_REGNUM + fpreg);
#undef I387_ST0_REGNUM
}
static void
i386_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
int regnum, gdb_byte *buf)
{
if (i386_mmx_regnum_p (gdbarch, regnum))
{
gdb_byte mmx_buf[MAX_REGISTER_SIZE];
int fpnum = i386_mmx_regnum_to_fp_regnum (regcache, regnum);
regcache_raw_read (regcache, fpnum, mmx_buf);
memcpy (buf, mmx_buf, register_size (gdbarch, regnum));
}
else
regcache_raw_read (regcache, regnum, buf);
}
static void
i386_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
int regnum, const gdb_byte *buf)
{
if (i386_mmx_regnum_p (gdbarch, regnum))
{
gdb_byte mmx_buf[MAX_REGISTER_SIZE];
int fpnum = i386_mmx_regnum_to_fp_regnum (regcache, regnum);
regcache_raw_read (regcache, fpnum, mmx_buf);
memcpy (mmx_buf, buf, register_size (gdbarch, regnum));
regcache_raw_write (regcache, fpnum, mmx_buf);
}
else
regcache_raw_write (regcache, regnum, buf);
}
static int
i386_next_regnum (int regnum)
{
static int next_regnum[] =
{
I386_EDX_REGNUM,
I386_EBX_REGNUM,
I386_ECX_REGNUM,
I386_ESI_REGNUM,
-1, -1,
I386_EDI_REGNUM,
I386_EBP_REGNUM
};
if (regnum >= 0 && regnum < sizeof (next_regnum) / sizeof (next_regnum[0]))
return next_regnum[regnum];
return -1;
}
static int
i386_convert_register_p (int regnum, struct type *type)
{
int len = TYPE_LENGTH (type);
if (len > 4 && len % 4 == 0)
{
int last_regnum = regnum;
while (len > 4)
{
last_regnum = i386_next_regnum (last_regnum);
len -= 4;
}
if (last_regnum != -1)
return 1;
}
return i386_fp_regnum_p (regnum);
}
static void
i386_register_to_value (struct frame_info *frame, int regnum,
struct type *type, gdb_byte *to)
{
int len = TYPE_LENGTH (type);
if (i386_fp_regnum_p (regnum))
{
i387_register_to_value (frame, regnum, type, to);
return;
}
gdb_assert (len > 4 && len % 4 == 0);
while (len > 0)
{
gdb_assert (regnum != -1);
gdb_assert (register_size (current_gdbarch, regnum) == 4);
get_frame_register (frame, regnum, to);
regnum = i386_next_regnum (regnum);
len -= 4;
to += 4;
}
}
static void
i386_value_to_register (struct frame_info *frame, int regnum,
struct type *type, const gdb_byte *from)
{
int len = TYPE_LENGTH (type);
if (i386_fp_regnum_p (regnum))
{
i387_value_to_register (frame, regnum, type, from);
return;
}
gdb_assert (len > 4 && len % 4 == 0);
while (len > 0)
{
gdb_assert (regnum != -1);
gdb_assert (register_size (current_gdbarch, regnum) == 4);
put_frame_register (frame, regnum, from);
regnum = i386_next_regnum (regnum);
len -= 4;
from += 4;
}
}
void
i386_supply_gregset (const struct regset *regset, struct regcache *regcache,
int regnum, const void *gregs, size_t len)
{
const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
const gdb_byte *regs = gregs;
int i;
gdb_assert (len == tdep->sizeof_gregset);
for (i = 0; i < tdep->gregset_num_regs; i++)
{
if ((regnum == i || regnum == -1)
&& tdep->gregset_reg_offset[i] != -1)
regcache_raw_supply (regcache, i, regs + tdep->gregset_reg_offset[i]);
}
}
void
i386_collect_gregset (const struct regset *regset,
const struct regcache *regcache,
int regnum, void *gregs, size_t len)
{
const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
gdb_byte *regs = gregs;
int i;
gdb_assert (len == tdep->sizeof_gregset);
for (i = 0; i < tdep->gregset_num_regs; i++)
{
if ((regnum == i || regnum == -1)
&& tdep->gregset_reg_offset[i] != -1)
regcache_raw_collect (regcache, i, regs + tdep->gregset_reg_offset[i]);
}
}
static void
i386_supply_fpregset (const struct regset *regset, struct regcache *regcache,
int regnum, const void *fpregs, size_t len)
{
const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
if (len == I387_SIZEOF_FXSAVE)
{
i387_supply_fxsave (regcache, regnum, fpregs);
return;
}
gdb_assert (len == tdep->sizeof_fpregset);
i387_supply_fsave (regcache, regnum, fpregs);
}
static void
i386_collect_fpregset (const struct regset *regset,
const struct regcache *regcache,
int regnum, void *fpregs, size_t len)
{
const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
if (len == I387_SIZEOF_FXSAVE)
{
i387_collect_fxsave (regcache, regnum, fpregs);
return;
}
gdb_assert (len == tdep->sizeof_fpregset);
i387_collect_fsave (regcache, regnum, fpregs);
}
const struct regset *
i386_regset_from_core_section (struct gdbarch *gdbarch,
const char *sect_name, size_t sect_size)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
if (strcmp (sect_name, ".reg") == 0 && sect_size == tdep->sizeof_gregset)
{
if (tdep->gregset == NULL)
tdep->gregset = regset_alloc (gdbarch, i386_supply_gregset,
i386_collect_gregset);
return tdep->gregset;
}
if ((strcmp (sect_name, ".reg2") == 0 && sect_size == tdep->sizeof_fpregset)
|| (strcmp (sect_name, ".reg-xfp") == 0
&& sect_size == I387_SIZEOF_FXSAVE))
{
if (tdep->fpregset == NULL)
tdep->fpregset = regset_alloc (gdbarch, i386_supply_fpregset,
i386_collect_fpregset);
return tdep->fpregset;
}
return NULL;
}
static int
i386_sigtramp_p (struct frame_info *next_frame)
{
CORE_ADDR pc = frame_pc_unwind (next_frame);
char *name;
find_pc_partial_function_no_inlined (pc, &name, NULL, NULL);
return (name && strcmp ("_sigtramp", name) == 0);
}
static int
i386_print_insn (bfd_vma pc, struct disassemble_info *info)
{
gdb_assert (disassembly_flavor == att_flavor
|| disassembly_flavor == intel_flavor);
info->disassembler_options = (char *) disassembly_flavor;
info->mach = gdbarch_bfd_arch_info (current_gdbarch)->mach;
return print_insn_i386 (pc, info);
}
static struct reggroup *i386_sse_reggroup;
static struct reggroup *i386_mmx_reggroup;
static void
i386_init_reggroups (void)
{
i386_sse_reggroup = reggroup_new ("sse", USER_REGGROUP);
i386_mmx_reggroup = reggroup_new ("mmx", USER_REGGROUP);
}
static void
i386_add_reggroups (struct gdbarch *gdbarch)
{
reggroup_add (gdbarch, i386_sse_reggroup);
reggroup_add (gdbarch, i386_mmx_reggroup);
reggroup_add (gdbarch, general_reggroup);
reggroup_add (gdbarch, float_reggroup);
reggroup_add (gdbarch, all_reggroup);
reggroup_add (gdbarch, save_reggroup);
reggroup_add (gdbarch, restore_reggroup);
reggroup_add (gdbarch, vector_reggroup);
reggroup_add (gdbarch, system_reggroup);
}
int
i386_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
struct reggroup *group)
{
int sse_regnum_p = (i386_sse_regnum_p (gdbarch, regnum)
|| i386_mxcsr_regnum_p (gdbarch, regnum));
int fp_regnum_p = (i386_fp_regnum_p (regnum)
|| i386_fpc_regnum_p (regnum));
int mmx_regnum_p = (i386_mmx_regnum_p (gdbarch, regnum));
if (group == i386_mmx_reggroup)
return mmx_regnum_p;
if (group == i386_sse_reggroup)
return sse_regnum_p;
if (group == vector_reggroup)
return (mmx_regnum_p || sse_regnum_p);
if (group == float_reggroup)
return fp_regnum_p;
if (group == general_reggroup)
return (!fp_regnum_p && !mmx_regnum_p && !sse_regnum_p);
return default_register_reggroup_p (gdbarch, regnum, group);
}
static CORE_ADDR
i386_fetch_pointer_argument (struct frame_info *frame, int argi,
struct type *type)
{
CORE_ADDR sp = get_frame_register_unsigned (frame, I386_ESP_REGNUM);
return read_memory_unsigned_integer (sp + (4 * (argi + 1)), 4);
}
static struct gdbarch *
i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
struct gdbarch_tdep *tdep;
struct gdbarch *gdbarch;
arches = gdbarch_list_lookup_by_info (arches, &info);
if (arches != NULL)
return arches->gdbarch;
tdep = XMALLOC (struct gdbarch_tdep);
gdbarch = gdbarch_alloc (&info, tdep);
tdep->wordsize = 4;
tdep->gregset = NULL;
tdep->gregset_reg_offset = NULL;
tdep->gregset_num_regs = I386_NUM_GREGS;
tdep->sizeof_gregset = 0;
tdep->fpregset = NULL;
tdep->sizeof_fpregset = I387_SIZEOF_FSAVE;
tdep->st0_regnum = I386_ST0_REGNUM;
tdep->mm0_regnum = 0;
tdep->num_xmm_regs = I386_NUM_XREGS - 1;
tdep->jb_pc_offset = -1;
tdep->struct_return = pcc_struct_return;
tdep->sigtramp_start = 0;
tdep->sigtramp_end = 0;
tdep->sigtramp_p = i386_sigtramp_p;
tdep->sigcontext_addr = NULL;
tdep->sc_reg_offset = NULL;
tdep->sc_pc_offset = -1;
tdep->sc_sp_offset = -1;
set_gdbarch_long_double_format (gdbarch, &floatformat_i387_ext);
set_gdbarch_long_double_bit (gdbarch, 128);
set_gdbarch_num_regs (gdbarch, I386_SSE_NUM_REGS);
set_gdbarch_register_name (gdbarch, i386_register_name);
set_gdbarch_register_type (gdbarch, i386_register_type);
set_gdbarch_sp_regnum (gdbarch, I386_ESP_REGNUM);
set_gdbarch_pc_regnum (gdbarch, I386_EIP_REGNUM);
set_gdbarch_ps_regnum (gdbarch, I386_EFLAGS_REGNUM);
set_gdbarch_fp0_regnum (gdbarch, I386_ST0_REGNUM);
set_gdbarch_deprecated_fp_regnum (gdbarch, I386_EBP_REGNUM);
set_gdbarch_stab_reg_to_regnum (gdbarch, i386_dbx_reg_to_regnum);
set_gdbarch_sdb_reg_to_regnum (gdbarch, i386_dbx_reg_to_regnum);
set_gdbarch_dwarf_reg_to_regnum (gdbarch, i386_svr4_reg_to_regnum);
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, i386_svr4_reg_to_regnum);
set_gdbarch_print_float_info (gdbarch, i387_print_float_info);
set_gdbarch_get_longjmp_target (gdbarch, i386_get_longjmp_target);
set_gdbarch_push_dummy_call (gdbarch, i386_push_dummy_call);
set_gdbarch_convert_register_p (gdbarch, i386_convert_register_p);
set_gdbarch_register_to_value (gdbarch, i386_register_to_value);
set_gdbarch_value_to_register (gdbarch, i386_value_to_register);
set_gdbarch_return_value (gdbarch, i386_return_value);
set_gdbarch_skip_prologue (gdbarch, i386_skip_prologue);
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
set_gdbarch_breakpoint_from_pc (gdbarch, i386_breakpoint_from_pc);
set_gdbarch_decr_pc_after_break (gdbarch, 1);
set_gdbarch_frame_args_skip (gdbarch, 8);
set_gdbarch_num_pseudo_regs (gdbarch, i386_num_mmx_regs);
set_gdbarch_pseudo_register_read (gdbarch, i386_pseudo_register_read);
set_gdbarch_pseudo_register_write (gdbarch, i386_pseudo_register_write);
set_gdbarch_print_insn (gdbarch, i386_print_insn);
set_gdbarch_unwind_dummy_id (gdbarch, i386_unwind_dummy_id);
set_gdbarch_unwind_pc (gdbarch, i386_unwind_pc);
i386_add_reggroups (gdbarch);
set_gdbarch_register_reggroup_p (gdbarch, i386_register_reggroup_p);
set_gdbarch_fetch_pointer_argument (gdbarch, i386_fetch_pointer_argument);
frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
frame_base_set_default (gdbarch, &i386_frame_base);
set_gdbarch_adjust_ehframe_regnum (gdbarch, i386_adjust_ehframe_regnum);
gdbarch_init_osabi (info, gdbarch);
frame_unwind_append_sniffer (gdbarch, i386_sigtramp_frame_sniffer);
frame_unwind_append_sniffer (gdbarch, i386_frame_sniffer);
if (tdep->gregset_reg_offset
&& !gdbarch_regset_from_core_section_p (gdbarch))
set_gdbarch_regset_from_core_section (gdbarch,
i386_regset_from_core_section);
if (tdep->mm0_regnum == 0)
tdep->mm0_regnum = gdbarch_num_regs (gdbarch);
return gdbarch;
}
static void
maintenance_i386_prologue_parser (char *arg, int from_tty)
{
char **argv;
CORE_ADDR start_address, end_address;
CORE_ADDR parsed_to;
int argc;
struct cleanup *cleanups;
struct x86_frame_cache *cache;
struct minimal_symbol *func;
int parse_failed = 0;
if (arg == NULL || arg[0] == '\0')
return;
argv = buildargv (arg);
if (argv == NULL)
return;
cleanups = make_cleanup_freeargv (argv);
for (argc = 0; argv[argc] != NULL && argv[argc][0] != '\0'; argc++)
;
if (argc == 0)
{
do_cleanups (cleanups);
return;
}
start_address = parse_and_eval_address (argv[0]);
if (argc == 2)
end_address = strtoul (argv[1], NULL, 16);
else
end_address = start_address + 48;
if (gdbarch_lookup_osabi (exec_bfd) == GDB_OSABI_DARWIN64)
cache = x86_alloc_frame_cache (8);
else
cache = x86_alloc_frame_cache (4);
parsed_to = x86_analyze_prologue (start_address, end_address + 1, cache);
func = lookup_minimal_symbol_by_pc_section (start_address, NULL);
printf_filtered ("Analyzing the prologue of '%s' 0x%s.\n",
SYMBOL_LINKAGE_NAME (func),
paddr_nz (SYMBOL_VALUE_ADDRESS (func)));
if (func != lookup_minimal_symbol_by_pc_section (parsed_to, NULL))
{
printf_filtered ("Prologue scanner went to 0x%s (off the end of '%s')"
" trying to\nfind a prologue. %s is frameless?\n",
paddr_nz (parsed_to),
SYMBOL_LINKAGE_NAME (func), SYMBOL_LINKAGE_NAME (func));
parse_failed = 1;
}
else
{
printf_filtered ("Prologue parser parsed to address 0x%s (%d bytes)",
paddr_nz (parsed_to), (int) (parsed_to - start_address));
if (parsed_to == end_address -1)
printf_filtered (" which is the entire length of the function\n");
else
{
printf_filtered (".\n");
if (cache->saved_regs[cache->ebp_regnum] == -1)
{
printf_filtered ("Didn't find push %%ebp and didn't parse the full range: prologue parse failed (frameless function?)\n");
parse_failed = 1;
}
}
}
printf_filtered ("\n");
if (cache->saved_regs[cache->ebp_regnum] == -1)
{
printf_filtered ("Did not find the push %%ebp\n");
parse_failed = 1;
}
else
printf_filtered ("Found push %%ebp\n");
if (cache->ebp_is_frame_pointer == 0)
{
printf_filtered ("Did not find mov %%esp, %%ebp\n");
parse_failed = 1;
}
if (cache->ebp_is_frame_pointer == 1)
printf_filtered ("Found mov %%esp, %%ebp\n");
if (parse_failed)
printf_filtered ("\nFAILED TO PARSE func %s startaddr 0x%s\n\n",
SYMBOL_LINKAGE_NAME (func), paddr_nz (start_address));
do_cleanups (cleanups);
}
void _initialize_i386_tdep (void);
void
_initialize_i386_tdep (void)
{
register_gdbarch_init (bfd_arch_i386, i386_gdbarch_init);
builtin_type_vec128i_big = build_builtin_type_vec128i_big ();
add_setshow_enum_cmd ("disassembly-flavor", no_class, valid_flavors,
&disassembly_flavor, _("\
Set the disassembly flavor."), _("\
Show the disassembly flavor."), _("\
The valid values are \"att\" and \"intel\", and the default value is \"att\"."),
NULL,
NULL,
&setlist, &showlist);
add_cmd ("i386-prologue-parser", class_maintenance,
maintenance_i386_prologue_parser,
"Run the i386 prologue analyzer on a function.\n"
"arg1 is start address of function\n"
"arg2 is optional end-address, defaulting to startaddr + 32 bytes.",
&maintenancelist);
add_setshow_enum_cmd ("struct-convention", no_class, valid_conventions,
&struct_convention, _("\
Set the convention for returning small structs."), _("\
Show the convention for returning small structs."), _("\
Valid values are \"default\", \"pcc\" and \"reg\", and the default value\n\
is \"default\"."),
NULL,
NULL,
&setlist, &showlist);
i386_init_reggroups ();
}