/* Intel 386 target-dependent stuff.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
- 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
This file is part of GDB.
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
+#include "opcode/i386.h"
#include "arch-utils.h"
#include "command.h"
#include "dummy-frame.h"
\f
/* Displaced instruction handling. */
+/* Skip the legacy instruction prefixes in INSN.
+ Not all prefixes are valid for any particular insn
+ but we needn't care, the insn will fault if it's invalid.
+ The result is a pointer to the first opcode byte,
+ or NULL if we run off the end of the buffer. */
+
+static gdb_byte *
+i386_skip_prefixes (gdb_byte *insn, size_t max_len)
+{
+ gdb_byte *end = insn + max_len;
+
+ while (insn < end)
+ {
+ switch (*insn)
+ {
+ case DATA_PREFIX_OPCODE:
+ case ADDR_PREFIX_OPCODE:
+ case CS_PREFIX_OPCODE:
+ case DS_PREFIX_OPCODE:
+ case ES_PREFIX_OPCODE:
+ case FS_PREFIX_OPCODE:
+ case GS_PREFIX_OPCODE:
+ case SS_PREFIX_OPCODE:
+ case LOCK_PREFIX_OPCODE:
+ case REPE_PREFIX_OPCODE:
+ case REPNE_PREFIX_OPCODE:
+ ++insn;
+ continue;
+ default:
+ return insn;
+ }
+ }
+
+ return NULL;
+}
static int
-i386_absolute_jmp_p (gdb_byte *insn)
+i386_absolute_jmp_p (const gdb_byte *insn)
{
/* jmp far (absolute address in operand) */
if (insn[0] == 0xea)
}
static int
-i386_absolute_call_p (gdb_byte *insn)
+i386_absolute_call_p (const gdb_byte *insn)
{
/* call far, absolute */
if (insn[0] == 0x9a)
}
static int
-i386_ret_p (gdb_byte *insn)
+i386_ret_p (const gdb_byte *insn)
{
switch (insn[0])
{
}
static int
-i386_call_p (gdb_byte *insn)
+i386_call_p (const gdb_byte *insn)
{
if (i386_absolute_call_p (insn))
return 1;
return 0;
}
-static int
-i386_breakpoint_p (gdb_byte *insn)
-{
- return insn[0] == 0xcc; /* int 3 */
-}
-
/* Return non-zero if INSN is a system call, and set *LENGTHP to its
length in bytes. Otherwise, return zero. */
+
static int
-i386_syscall_p (gdb_byte *insn, ULONGEST *lengthp)
+i386_syscall_p (const gdb_byte *insn, ULONGEST *lengthp)
{
if (insn[0] == 0xcd)
{
/* Fix up the state of registers and memory after having single-stepped
a displaced instruction. */
+
void
i386_displaced_step_fixup (struct gdbarch *gdbarch,
struct displaced_step_closure *closure,
/* Since we use simple_displaced_step_copy_insn, our closure is a
copy of the instruction. */
gdb_byte *insn = (gdb_byte *) closure;
+ /* The start of the insn, needed in case we see some prefixes. */
+ gdb_byte *insn_start = insn;
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog,
/* Relocate the %eip, if necessary. */
+ /* The instruction recognizers we use assume any leading prefixes
+ have been skipped. */
+ {
+ /* This is the size of the buffer in closure. */
+ size_t max_insn_len = gdbarch_max_insn_length (gdbarch);
+ gdb_byte *opcode = i386_skip_prefixes (insn, max_insn_len);
+ /* If there are too many prefixes, just ignore the insn.
+ It will fault when run. */
+ if (opcode != NULL)
+ insn = opcode;
+ }
+
/* Except in the case of absolute or indirect jump or call
instructions, or a return instruction, the new eip is relative to
the displaced instruction; make it relative. Well, signal
it unrelocated. Goodness help us if there are PC-relative
system calls. */
if (i386_syscall_p (insn, &insn_len)
- && orig_eip != to + insn_len)
+ && orig_eip != to + (insn - insn_start) + insn_len)
{
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog,
{
ULONGEST eip = (orig_eip - insn_offset) & 0xffffffffUL;
- /* If we have stepped over a breakpoint, set the %eip to
- point at the breakpoint instruction itself.
-
- (gdbarch_decr_pc_after_break was never something the core
- of GDB should have been concerned with; arch-specific
- code should be making PC values consistent before
- presenting them to GDB.) */
- if (i386_breakpoint_p (insn))
- {
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog,
- "displaced: stepped breakpoint\n");
- eip--;
- }
+ /* If we just stepped over a breakpoint insn, we don't backup
+ the pc on purpose; this is to match behaviour without
+ stepping. */
regcache_cooked_write_unsigned (regs, I386_EIP_REGNUM, eip);
paddr_nz (retaddr));
}
}
-
-
\f
#ifdef I386_REGNO_TO_SYMMETRY
#error "The Sequent Symmetry is no longer supported."
/* Saved registers. */
CORE_ADDR saved_regs[I386_NUM_SAVED_REGS];
CORE_ADDR saved_sp;
- int stack_align;
+ int saved_sp_reg;
int pc_in_eax;
/* Stack space reserved for local variables. */
for (i = 0; i < I386_NUM_SAVED_REGS; i++)
cache->saved_regs[i] = -1;
cache->saved_sp = 0;
- cache->stack_align = 0;
+ cache->saved_sp_reg = -1;
cache->pc_in_eax = 0;
/* Frameless until proven otherwise. */
i386_analyze_stack_align (CORE_ADDR pc, CORE_ADDR current_pc,
struct i386_frame_cache *cache)
{
- /* The register used by the compiler to perform the stack re-alignment
- is, in order of preference, either %ecx, %edx, or %eax. GCC should
- never use %ebx as it always treats it as callee-saved, whereas
- the compiler can only use caller-saved registers. */
- static const gdb_byte insns_ecx[10] = {
- 0x8d, 0x4c, 0x24, 0x04, /* leal 4(%esp), %ecx */
- 0x83, 0xe4, 0xf0, /* andl $-16, %esp */
- 0xff, 0x71, 0xfc /* pushl -4(%ecx) */
+ /* There are 2 code sequences to re-align stack before the frame
+ gets set up:
+
+ 1. Use a caller-saved saved register:
+
+ leal 4(%esp), %reg
+ andl $-XXX, %esp
+ pushl -4(%reg)
+
+ 2. Use a callee-saved saved register:
+
+ pushl %reg
+ leal 8(%esp), %reg
+ andl $-XXX, %esp
+ pushl -4(%reg)
+
+ "andl $-XXX, %esp" can be either 3 bytes or 6 bytes:
+
+ 0x83 0xe4 0xf0 andl $-16, %esp
+ 0x81 0xe4 0x00 0xff 0xff 0xff andl $-256, %esp
+ */
+
+ gdb_byte buf[14];
+ int reg;
+ int offset, offset_and;
+ static int regnums[8] = {
+ I386_EAX_REGNUM, /* %eax */
+ I386_ECX_REGNUM, /* %ecx */
+ I386_EDX_REGNUM, /* %edx */
+ I386_EBX_REGNUM, /* %ebx */
+ I386_ESP_REGNUM, /* %esp */
+ I386_EBP_REGNUM, /* %ebp */
+ I386_ESI_REGNUM, /* %esi */
+ I386_EDI_REGNUM /* %edi */
};
- static const gdb_byte insns_edx[10] = {
- 0x8d, 0x54, 0x24, 0x04, /* leal 4(%esp), %edx */
- 0x83, 0xe4, 0xf0, /* andl $-16, %esp */
- 0xff, 0x72, 0xfc /* pushl -4(%edx) */
- };
- static const gdb_byte insns_eax[10] = {
- 0x8d, 0x44, 0x24, 0x04, /* leal 4(%esp), %eax */
- 0x83, 0xe4, 0xf0, /* andl $-16, %esp */
- 0xff, 0x70, 0xfc /* pushl -4(%eax) */
- };
- gdb_byte buf[10];
- if (target_read_memory (pc, buf, sizeof buf)
- || (memcmp (buf, insns_ecx, sizeof buf) != 0
- && memcmp (buf, insns_edx, sizeof buf) != 0
- && memcmp (buf, insns_eax, sizeof buf) != 0))
+ if (target_read_memory (pc, buf, sizeof buf))
+ return pc;
+
+ /* Check caller-saved saved register. The first instruction has
+ to be "leal 4(%esp), %reg". */
+ if (buf[0] == 0x8d && buf[2] == 0x24 && buf[3] == 0x4)
+ {
+ /* MOD must be binary 10 and R/M must be binary 100. */
+ if ((buf[1] & 0xc7) != 0x44)
+ return pc;
+
+ /* REG has register number. */
+ reg = (buf[1] >> 3) & 7;
+ offset = 4;
+ }
+ else
+ {
+ /* Check callee-saved saved register. The first instruction
+ has to be "pushl %reg". */
+ if ((buf[0] & 0xf8) != 0x50)
+ return pc;
+
+ /* Get register. */
+ reg = buf[0] & 0x7;
+
+ /* The next instruction has to be "leal 8(%esp), %reg". */
+ if (buf[1] != 0x8d || buf[3] != 0x24 || buf[4] != 0x8)
+ return pc;
+
+ /* MOD must be binary 10 and R/M must be binary 100. */
+ if ((buf[2] & 0xc7) != 0x44)
+ return pc;
+
+ /* REG has register number. Registers in pushl and leal have to
+ be the same. */
+ if (reg != ((buf[2] >> 3) & 7))
+ return pc;
+
+ offset = 5;
+ }
+
+ /* Rigister can't be %esp nor %ebp. */
+ if (reg == 4 || reg == 5)
+ return pc;
+
+ /* The next instruction has to be "andl $-XXX, %esp". */
+ if (buf[offset + 1] != 0xe4
+ || (buf[offset] != 0x81 && buf[offset] != 0x83))
+ return pc;
+
+ offset_and = offset;
+ offset += buf[offset] == 0x81 ? 6 : 3;
+
+ /* The next instruction has to be "pushl -4(%reg)". 8bit -4 is
+ 0xfc. REG must be binary 110 and MOD must be binary 01. */
+ if (buf[offset] != 0xff
+ || buf[offset + 2] != 0xfc
+ || (buf[offset + 1] & 0xf8) != 0x70)
+ return pc;
+
+ /* R/M has register. Registers in leal and pushl have to be the
+ same. */
+ if (reg != (buf[offset + 1] & 7))
return pc;
- if (current_pc > pc + 4)
- cache->stack_align = 1;
+ if (current_pc > pc + offset_and)
+ cache->saved_sp_reg = regnums[reg];
- return min (pc + 10, current_pc);
+ return min (pc + offset + 3, current_pc);
}
/* Maximum instruction length we need to handle. */
gdb_byte buf[8];
frame_unwind_register (next_frame, gdbarch_pc_regnum (gdbarch), buf);
- return extract_typed_address (buf, builtin_type_void_func_ptr);
+ return extract_typed_address (buf, builtin_type (gdbarch)->builtin_func_ptr);
}
\f
if (cache->pc != 0)
i386_analyze_prologue (cache->pc, get_frame_pc (this_frame), cache);
- if (cache->stack_align)
+ if (cache->saved_sp_reg != -1)
{
- /* Saved stack pointer has been saved in %ecx. */
- get_frame_register (this_frame, I386_ECX_REGNUM, buf);
+ /* Saved stack pointer has been saved. */
+ get_frame_register (this_frame, cache->saved_sp_reg, buf);
cache->saved_sp = extract_unsigned_integer(buf, 4);
}
frame by looking at the stack pointer. For truly "frameless"
functions this might work too. */
- if (cache->stack_align)
+ if (cache->saved_sp_reg != -1)
{
/* We're halfway aligning the stack. */
cache->base = ((cache->saved_sp - 4) & 0xfffffff0) - 4;
(i386_frame_this_id, i386_sigtramp_frame_this_id,
i386_dummy_id). It's there, since all frame unwinders for
a given target have to agree (within a certain margin) on the
- definition of the stack address of a frame. Otherwise
- frame_id_inner() won't work correctly. Since DWARF2/GCC uses the
+ definition of the stack address of a frame. Otherwise frame id
+ comparison might not work correctly. Since DWARF2/GCC uses the
stack address *before* the function call as a frame's CFA. On
the i386, when %ebp is used as a frame pointer, the offset
between the contents %ebp and the CFA as defined by GCC. */
append_composite_type_field (t, "v8_int8",
init_vector_type (builtin_type_int8, 8));
- TYPE_FLAGS (t) |= TYPE_FLAG_VECTOR;
+ TYPE_VECTOR (t) = 1;
TYPE_NAME (t) = "builtin_type_vec64i";
tdep->i386_mmx_type = t;
}
t = init_composite_type ("__gdb_builtin_type_vec128i", TYPE_CODE_UNION);
append_composite_type_field (t, "v4_float",
- init_vector_type (builtin_type_float, 4));
+ init_vector_type (builtin_type (gdbarch)
+ ->builtin_float, 4));
append_composite_type_field (t, "v2_double",
- init_vector_type (builtin_type_double, 2));
+ init_vector_type (builtin_type (gdbarch)
+ ->builtin_double, 2));
append_composite_type_field (t, "v16_int8",
init_vector_type (builtin_type_int8, 16));
append_composite_type_field (t, "v8_int16",
init_vector_type (builtin_type_int64, 2));
append_composite_type_field (t, "uint128", builtin_type_int128);
- TYPE_FLAGS (t) |= TYPE_FLAG_VECTOR;
+ TYPE_VECTOR (t) = 1;
TYPE_NAME (t) = "builtin_type_vec128i";
tdep->i386_sse_type = t;
}
i386_register_type (struct gdbarch *gdbarch, int regnum)
{
if (regnum == I386_EIP_REGNUM)
- return builtin_type_void_func_ptr;
+ return builtin_type (gdbarch)->builtin_func_ptr;
if (regnum == I386_EFLAGS_REGNUM)
return i386_eflags_type;
if (regnum == I386_EBP_REGNUM || regnum == I386_ESP_REGNUM)
- return builtin_type_void_data_ptr;
+ return builtin_type (gdbarch)->builtin_data_ptr;
if (i386_fp_regnum_p (gdbarch, regnum))
return builtin_type_i387_ext;
if (regnum == I387_MXCSR_REGNUM (gdbarch_tdep (gdbarch)))
return i386_mxcsr_type;
- return builtin_type_int;
+ return builtin_type (gdbarch)->builtin_int;
}
/* Map a cooked register onto a raw register or memory. For the i386,
/* Return whether the THIS_FRAME corresponds to a sigtramp
routine. */
-static int
+int
i386_sigtramp_p (struct frame_info *this_frame)
{
CORE_ADDR pc = get_frame_pc (this_frame);
return read_memory_unsigned_integer (sp + (4 * (argi + 1)), 4);
}
+static void
+i386_skip_permanent_breakpoint (struct regcache *regcache)
+{
+ CORE_ADDR current_pc = regcache_read_pc (regcache);
+
+ /* On i386, breakpoint is exactly 1 byte long, so we just
+ adjust the PC in the regcache. */
+ current_pc += 1;
+ regcache_write_pc (regcache, current_pc);
+}
+
+
\f
static struct gdbarch *
i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
if (tdep->mm0_regnum == 0)
tdep->mm0_regnum = gdbarch_num_regs (gdbarch);
+ set_gdbarch_skip_permanent_breakpoint (gdbarch,
+ i386_skip_permanent_breakpoint);
+
return gdbarch;
}