/* Target-dependent code for AMD64.
- Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- Free Software Foundation, Inc.
+ Copyright (C) 2001-2012 Free Software Foundation, Inc.
Contributed by Jiri Smid, SuSE Labs.
#include "symfile.h"
#include "disasm.h"
#include "gdb_assert.h"
-
+#include "exceptions.h"
#include "amd64-tdep.h"
#include "i387-tdep.h"
#include "features/i386/amd64.c"
#include "features/i386/amd64-avx.c"
+#include "ax.h"
+#include "ax-gdb.h"
+
/* Note that the AMD64 architecture was previously known as x86-64.
The latter is (forever) engraved into the canonical system name as
returned by config.guess, and used as the name for the AMD64 port
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d"
};
-/* Return the name of register REGNUM, or the empty string if it is
- an anonymous register. */
-
-static const char *
-amd64_register_name (struct gdbarch *gdbarch, int regnum)
-{
- /* Hide the upper YMM registers. */
- if (i386_ymmh_regnum_p (gdbarch, regnum))
- return "";
-
- return tdesc_register_name (gdbarch, regnum);
-}
-
/* Return the name of register REGNUM. */
static const char *
return i386_pseudo_register_name (gdbarch, regnum);
}
-static void
-amd64_pseudo_register_read (struct gdbarch *gdbarch,
- struct regcache *regcache,
- int regnum, gdb_byte *buf)
+static struct value *
+amd64_pseudo_register_read_value (struct gdbarch *gdbarch,
+ struct regcache *regcache,
+ int regnum)
{
gdb_byte raw_buf[MAX_REGISTER_SIZE];
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ enum register_status status;
+ struct value *result_value;
+ gdb_byte *buf;
+
+ result_value = allocate_value (register_type (gdbarch, regnum));
+ VALUE_LVAL (result_value) = lval_register;
+ VALUE_REGNUM (result_value) = regnum;
+ buf = value_contents_raw (result_value);
if (i386_byte_regnum_p (gdbarch, regnum))
{
if (gpnum >= AMD64_NUM_LOWER_BYTE_REGS)
{
/* Special handling for AH, BH, CH, DH. */
- regcache_raw_read (regcache,
- gpnum - AMD64_NUM_LOWER_BYTE_REGS, raw_buf);
- memcpy (buf, raw_buf + 1, 1);
+ status = regcache_raw_read (regcache,
+ gpnum - AMD64_NUM_LOWER_BYTE_REGS,
+ raw_buf);
+ if (status == REG_VALID)
+ memcpy (buf, raw_buf + 1, 1);
+ else
+ mark_value_bytes_unavailable (result_value, 0,
+ TYPE_LENGTH (value_type (result_value)));
}
else
{
- regcache_raw_read (regcache, gpnum, raw_buf);
- memcpy (buf, raw_buf, 1);
+ status = regcache_raw_read (regcache, gpnum, raw_buf);
+ if (status == REG_VALID)
+ memcpy (buf, raw_buf, 1);
+ else
+ mark_value_bytes_unavailable (result_value, 0,
+ TYPE_LENGTH (value_type (result_value)));
}
}
else if (i386_dword_regnum_p (gdbarch, regnum))
{
int gpnum = regnum - tdep->eax_regnum;
/* Extract (always little endian). */
- regcache_raw_read (regcache, gpnum, raw_buf);
- memcpy (buf, raw_buf, 4);
+ status = regcache_raw_read (regcache, gpnum, raw_buf);
+ if (status == REG_VALID)
+ memcpy (buf, raw_buf, 4);
+ else
+ mark_value_bytes_unavailable (result_value, 0,
+ TYPE_LENGTH (value_type (result_value)));
}
else
- i386_pseudo_register_read (gdbarch, regcache, regnum, buf);
+ i386_pseudo_register_read_into_value (gdbarch, regcache, regnum,
+ result_value);
+
+ return result_value;
}
static void
if (class[0] == AMD64_MEMORY || class[1] == AMD64_MEMORY)
class[0] = class[1] = AMD64_MEMORY;
- /* Rule (b): If SSEUP is not preceeded by SSE, it is converted to
+ /* Rule (b): If SSEUP is not preceded by SSE, it is converted to
SSE. */
if (class[0] == AMD64_SSEUP)
class[0] = AMD64_SSE;
/* 2. If the type has class MEMORY, then the caller provides space
for the return value and passes the address of this storage in
- %rdi as if it were the first argument to the function. In effect,
+ %rdi as if it were the first argument to the function. In effect,
this address becomes a hidden first argument.
On return %rax will contain the address that has been passed in
/* Pass "hidden" argument". */
if (struct_return)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
/* The "hidden" argument is passed throught the first argument
register. */
const int arg_regnum = tdep->call_dummy_integer_regs[0];
if (have_sib)
{
int base = SIB_BASE_FIELD (details->raw_insn[details->modrm_offset + 1]);
- int index = SIB_INDEX_FIELD (details->raw_insn[details->modrm_offset + 1]);
+ int idx = SIB_INDEX_FIELD (details->raw_insn[details->modrm_offset + 1]);
used_regs_mask |= 1 << base;
- used_regs_mask |= 1 << index;
+ used_regs_mask |= 1 << idx;
}
else
{
struct regcache *regs)
{
int len = gdbarch_max_insn_length (gdbarch);
- /* Extra space for sentinels so fixup_{riprel,displaced_copy don't have to
+ /* Extra space for sentinels so fixup_{riprel,displaced_copy} don't have to
continually watch for running off the end of the buffer. */
int fixup_sentinel_space = len;
struct displaced_step_closure *dsc =
*to += len;
}
-void
+static void
amd64_relocate_instruction (struct gdbarch *gdbarch,
CORE_ADDR *to, CORE_ADDR oldloc)
{
/* Where "ret" in the original code will return to. */
ret_addr = oldloc + insn_length;
- push_buf[0] = 0x68; /* pushq $... */
+ push_buf[0] = 0x68; /* pushq $... */
memcpy (&push_buf[1], &ret_addr, 4);
/* Push the push. */
append_insns (to, 5, push_buf);
/* Adjust the destination offset. */
rel32 = extract_signed_integer (insn + 1, 4, byte_order);
newrel = (oldloc - *to) + rel32;
- store_signed_integer (insn + 1, 4, newrel, byte_order);
+ store_signed_integer (insn + 1, 4, byte_order, newrel);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "Adjusted insn rel32=%s at %s to"
+ " rel32=%s at %s\n",
+ hex_string (rel32), paddress (gdbarch, oldloc),
+ hex_string (newrel), paddress (gdbarch, *to));
/* Write the adjusted jump into its displaced location. */
append_insns (to, 5, insn);
{
rel32 = extract_signed_integer (insn + offset, 4, byte_order);
newrel = (oldloc - *to) + rel32;
- store_signed_integer (insn + offset, 4, newrel, byte_order);
+ store_signed_integer (insn + offset, 4, byte_order, newrel);
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog,
- "Adjusted insn rel32=0x%s at 0x%s to"
- " rel32=0x%s at 0x%s\n",
+ "Adjusted insn rel32=%s at %s to"
+ " rel32=%s at %s\n",
hex_string (rel32), paddress (gdbarch, oldloc),
hex_string (newrel), paddress (gdbarch, *to));
}
{
/* Base address. */
CORE_ADDR base;
+ int base_p;
CORE_ADDR sp_offset;
CORE_ADDR pc;
/* Base address. */
cache->base = 0;
+ cache->base_p = 0;
cache->sp_offset = -8;
cache->pc = 0;
return pc;
}
+/* Work around false termination of prologue - GCC PR debug/48827.
+
+ START_PC is the first instruction of a function, PC is its minimal already
+ determined advanced address. Function returns PC if it has nothing to do.
+
+ 84 c0 test %al,%al
+ 74 23 je after
+ <-- here is 0 lines advance - the false prologue end marker.
+ 0f 29 85 70 ff ff ff movaps %xmm0,-0x90(%rbp)
+ 0f 29 4d 80 movaps %xmm1,-0x80(%rbp)
+ 0f 29 55 90 movaps %xmm2,-0x70(%rbp)
+ 0f 29 5d a0 movaps %xmm3,-0x60(%rbp)
+ 0f 29 65 b0 movaps %xmm4,-0x50(%rbp)
+ 0f 29 6d c0 movaps %xmm5,-0x40(%rbp)
+ 0f 29 75 d0 movaps %xmm6,-0x30(%rbp)
+ 0f 29 7d e0 movaps %xmm7,-0x20(%rbp)
+ after: */
+
+static CORE_ADDR
+amd64_skip_xmm_prologue (CORE_ADDR pc, CORE_ADDR start_pc)
+{
+ struct symtab_and_line start_pc_sal, next_sal;
+ gdb_byte buf[4 + 8 * 7];
+ int offset, xmmreg;
+
+ if (pc == start_pc)
+ return pc;
+
+ start_pc_sal = find_pc_sect_line (start_pc, NULL, 0);
+ if (start_pc_sal.symtab == NULL
+ || producer_is_gcc_ge_4 (start_pc_sal.symtab->producer) < 6
+ || start_pc_sal.pc != start_pc || pc >= start_pc_sal.end)
+ return pc;
+
+ next_sal = find_pc_sect_line (start_pc_sal.end, NULL, 0);
+ if (next_sal.line != start_pc_sal.line)
+ return pc;
+
+ /* START_PC can be from overlayed memory, ignored here. */
+ if (target_read_memory (next_sal.pc - 4, buf, sizeof (buf)) != 0)
+ return pc;
+
+ /* test %al,%al */
+ if (buf[0] != 0x84 || buf[1] != 0xc0)
+ return pc;
+ /* je AFTER */
+ if (buf[2] != 0x74)
+ return pc;
+
+ offset = 4;
+ for (xmmreg = 0; xmmreg < 8; xmmreg++)
+ {
+ /* 0x0f 0x29 0b??000101 movaps %xmmreg?,-0x??(%rbp) */
+ if (buf[offset] != 0x0f || buf[offset + 1] != 0x29
+ || (buf[offset + 2] & 0x3f) != (xmmreg << 3 | 0x5))
+ return pc;
+
+ /* 0b01?????? */
+ if ((buf[offset + 2] & 0xc0) == 0x40)
+ {
+ /* 8-bit displacement. */
+ offset += 4;
+ }
+ /* 0b10?????? */
+ else if ((buf[offset + 2] & 0xc0) == 0x80)
+ {
+ /* 32-bit displacement. */
+ offset += 7;
+ }
+ else
+ return pc;
+ }
+
+ /* je AFTER */
+ if (offset - 4 != buf[3])
+ return pc;
+
+ return next_sal.end;
+}
+
/* Return PC of first real instruction. */
static CORE_ADDR
if (cache.frameless_p)
return start_pc;
- return pc;
+ return amd64_skip_xmm_prologue (pc, start_pc);
}
\f
/* Normal frames. */
-static struct amd64_frame_cache *
-amd64_frame_cache (struct frame_info *this_frame, void **this_cache)
+static void
+amd64_frame_cache_1 (struct frame_info *this_frame,
+ struct amd64_frame_cache *cache)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- struct amd64_frame_cache *cache;
gdb_byte buf[8];
int i;
- if (*this_cache)
- return *this_cache;
-
- cache = amd64_alloc_frame_cache ();
- *this_cache = cache;
-
cache->pc = get_frame_func (this_frame);
if (cache->pc != 0)
amd64_analyze_prologue (gdbarch, cache->pc, get_frame_pc (this_frame),
cache);
- if (cache->saved_sp_reg != -1)
- {
- /* Stack pointer has been saved. */
- get_frame_register (this_frame, cache->saved_sp_reg, buf);
- cache->saved_sp = extract_unsigned_integer(buf, 8, byte_order);
- }
-
if (cache->frameless_p)
{
/* We didn't find a valid frame. If we're at the start of a
if (cache->saved_sp_reg != -1)
{
+ /* Stack pointer has been saved. */
+ get_frame_register (this_frame, cache->saved_sp_reg, buf);
+ cache->saved_sp = extract_unsigned_integer (buf, 8, byte_order);
+
/* We're halfway aligning the stack. */
cache->base = ((cache->saved_sp - 8) & 0xfffffffffffffff0LL) - 8;
cache->saved_regs[AMD64_RIP_REGNUM] = cache->saved_sp - 8;
if (cache->saved_regs[i] != -1)
cache->saved_regs[i] += cache->base;
+ cache->base_p = 1;
+}
+
+static struct amd64_frame_cache *
+amd64_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+ volatile struct gdb_exception ex;
+ struct amd64_frame_cache *cache;
+
+ if (*this_cache)
+ return *this_cache;
+
+ cache = amd64_alloc_frame_cache ();
+ *this_cache = cache;
+
+ TRY_CATCH (ex, RETURN_MASK_ERROR)
+ {
+ amd64_frame_cache_1 (this_frame, cache);
+ }
+ if (ex.reason < 0 && ex.error != NOT_AVAILABLE_ERROR)
+ throw_exception (ex);
+
return cache;
}
+static enum unwind_stop_reason
+amd64_frame_unwind_stop_reason (struct frame_info *this_frame,
+ void **this_cache)
+{
+ struct amd64_frame_cache *cache =
+ amd64_frame_cache (this_frame, this_cache);
+
+ if (!cache->base_p)
+ return UNWIND_UNAVAILABLE;
+
+ /* This marks the outermost frame. */
+ if (cache->base == 0)
+ return UNWIND_OUTERMOST;
+
+ return UNWIND_NO_REASON;
+}
+
static void
amd64_frame_this_id (struct frame_info *this_frame, void **this_cache,
struct frame_id *this_id)
struct amd64_frame_cache *cache =
amd64_frame_cache (this_frame, this_cache);
+ if (!cache->base_p)
+ return;
+
/* This marks the outermost frame. */
if (cache->base == 0)
return;
static const struct frame_unwind amd64_frame_unwind =
{
NORMAL_FRAME,
+ amd64_frame_unwind_stop_reason,
amd64_frame_this_id,
amd64_frame_prev_register,
NULL,
default_frame_sniffer
};
\f
+/* Generate a bytecode expression to get the value of the saved PC. */
+
+static void
+amd64_gen_return_address (struct gdbarch *gdbarch,
+ struct agent_expr *ax, struct axs_value *value,
+ CORE_ADDR scope)
+{
+ /* The following sequence assumes the traditional use of the base
+ register. */
+ ax_reg (ax, AMD64_RBP_REGNUM);
+ ax_const_l (ax, 8);
+ ax_simple (ax, aop_add);
+ value->type = register_type (gdbarch, AMD64_RIP_REGNUM);
+ value->kind = axs_lvalue_memory;
+}
+\f
/* Signal trampolines. */
struct gdbarch *gdbarch = get_frame_arch (this_frame);
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ volatile struct gdb_exception ex;
struct amd64_frame_cache *cache;
CORE_ADDR addr;
gdb_byte buf[8];
cache = amd64_alloc_frame_cache ();
- get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
- cache->base = extract_unsigned_integer (buf, 8, byte_order) - 8;
+ TRY_CATCH (ex, RETURN_MASK_ERROR)
+ {
+ get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
+ cache->base = extract_unsigned_integer (buf, 8, byte_order) - 8;
+
+ addr = tdep->sigcontext_addr (this_frame);
+ gdb_assert (tdep->sc_reg_offset);
+ gdb_assert (tdep->sc_num_regs <= AMD64_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];
- addr = tdep->sigcontext_addr (this_frame);
- gdb_assert (tdep->sc_reg_offset);
- gdb_assert (tdep->sc_num_regs <= AMD64_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];
+ cache->base_p = 1;
+ }
+ if (ex.reason < 0 && ex.error != NOT_AVAILABLE_ERROR)
+ throw_exception (ex);
*this_cache = cache;
return cache;
}
+static enum unwind_stop_reason
+amd64_sigtramp_frame_unwind_stop_reason (struct frame_info *this_frame,
+ void **this_cache)
+{
+ struct amd64_frame_cache *cache =
+ amd64_sigtramp_frame_cache (this_frame, this_cache);
+
+ if (!cache->base_p)
+ return UNWIND_UNAVAILABLE;
+
+ return UNWIND_NO_REASON;
+}
+
static void
amd64_sigtramp_frame_this_id (struct frame_info *this_frame,
void **this_cache, struct frame_id *this_id)
struct amd64_frame_cache *cache =
amd64_sigtramp_frame_cache (this_frame, this_cache);
+ if (!cache->base_p)
+ return;
+
(*this_id) = frame_id_build (cache->base + 16, get_frame_pc (this_frame));
}
static const struct frame_unwind amd64_sigtramp_frame_unwind =
{
SIGTRAMP_FRAME,
+ amd64_sigtramp_frame_unwind_stop_reason,
amd64_sigtramp_frame_this_id,
amd64_sigtramp_frame_prev_register,
NULL,
amd64_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
{
gdb_byte insn;
+ struct symtab *symtab;
+
+ symtab = find_pc_symtab (pc);
+ if (symtab && symtab->epilogue_unwind_valid)
+ return 0;
if (target_read_memory (pc, &insn, 1))
return 0; /* Can't read memory at pc. */
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ volatile struct gdb_exception ex;
struct amd64_frame_cache *cache;
gdb_byte buf[8];
cache = amd64_alloc_frame_cache ();
*this_cache = cache;
- /* Cache base will be %esp plus cache->sp_offset (-8). */
- get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
- cache->base = extract_unsigned_integer (buf, 8,
- byte_order) + cache->sp_offset;
+ TRY_CATCH (ex, RETURN_MASK_ERROR)
+ {
+ /* Cache base will be %esp plus cache->sp_offset (-8). */
+ get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
+ cache->base = extract_unsigned_integer (buf, 8,
+ byte_order) + cache->sp_offset;
+
+ /* Cache pc will be the frame func. */
+ cache->pc = get_frame_pc (this_frame);
- /* Cache pc will be the frame func. */
- cache->pc = get_frame_pc (this_frame);
+ /* The saved %esp will be at cache->base plus 16. */
+ cache->saved_sp = cache->base + 16;
- /* The saved %esp will be at cache->base plus 16. */
- cache->saved_sp = cache->base + 16;
+ /* The saved %eip will be at cache->base plus 8. */
+ cache->saved_regs[AMD64_RIP_REGNUM] = cache->base + 8;
- /* The saved %eip will be at cache->base plus 8. */
- cache->saved_regs[AMD64_RIP_REGNUM] = cache->base + 8;
+ cache->base_p = 1;
+ }
+ if (ex.reason < 0 && ex.error != NOT_AVAILABLE_ERROR)
+ throw_exception (ex);
return cache;
}
+static enum unwind_stop_reason
+amd64_epilogue_frame_unwind_stop_reason (struct frame_info *this_frame,
+ void **this_cache)
+{
+ struct amd64_frame_cache *cache
+ = amd64_epilogue_frame_cache (this_frame, this_cache);
+
+ if (!cache->base_p)
+ return UNWIND_UNAVAILABLE;
+
+ return UNWIND_NO_REASON;
+}
+
static void
amd64_epilogue_frame_this_id (struct frame_info *this_frame,
void **this_cache,
struct amd64_frame_cache *cache = amd64_epilogue_frame_cache (this_frame,
this_cache);
+ if (!cache->base_p)
+ return;
+
(*this_id) = frame_id_build (cache->base + 8, cache->pc);
}
static const struct frame_unwind amd64_epilogue_frame_unwind =
{
NORMAL_FRAME,
+ amd64_epilogue_frame_unwind_stop_reason,
amd64_epilogue_frame_this_id,
amd64_frame_prev_register,
NULL,
/* Avoid wiring in the MMX registers for now. */
tdep->num_mmx_regs = 0;
- set_gdbarch_pseudo_register_read (gdbarch,
- amd64_pseudo_register_read);
+ set_gdbarch_pseudo_register_read_value (gdbarch,
+ amd64_pseudo_register_read_value);
set_gdbarch_pseudo_register_write (gdbarch,
amd64_pseudo_register_write);
set_tdesc_pseudo_register_name (gdbarch, amd64_pseudo_register_name);
- set_gdbarch_register_name (gdbarch, amd64_register_name);
-
/* AMD64 has an FPU and 16 SSE registers. */
tdep->st0_regnum = AMD64_ST0_REGNUM;
tdep->num_xmm_regs = 16;
set_gdbarch_get_longjmp_target (gdbarch, amd64_get_longjmp_target);
set_gdbarch_relocate_instruction (gdbarch, amd64_relocate_instruction);
+
+ set_gdbarch_gen_return_address (gdbarch, amd64_gen_return_address);
}
/* Provide a prototype to silence -Wmissing-prototypes. */
}
}
-/* Similar to amd64_collect_fxsave, but but use XSAVE extended state. */
+/* Similar to amd64_collect_fxsave, but use XSAVE extended state. */
void
amd64_collect_xsave (const struct regcache *regcache, int regnum,