/* Target-dependent code for SPARC.
- Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright (C) 2003-2014 Free Software Foundation, Inc.
This file is part of GDB.
#include "target.h"
#include "value.h"
-#include "gdb_assert.h"
-#include "gdb_string.h"
-
#include "sparc-tdep.h"
+#include "sparc-ravenscar-thread.h"
struct regset;
/* Sign extension macros. */
#define X_DISP22(i) ((X_IMM22 (i) ^ 0x200000) - 0x200000)
#define X_DISP19(i) ((((i) & 0x7ffff) ^ 0x40000) - 0x40000)
+#define X_DISP10(i) ((((((i) >> 11) && 0x300) | (((i) >> 5) & 0xff)) ^ 0x200) - 0x200)
#define X_SIMM13(i) ((((i) & 0x1fff) ^ 0x1000) - 0x1000)
+/* Macros to identify some instructions. */
+/* RETURN (RETT in V8) */
+#define X_RETTURN(i) ((X_OP (i) == 0x2) && (X_OP3 (i) == 0x39))
/* Fetch the instruction at PC. Instructions are always big-endian
even if the processor operates in little-endian mode. */
return ((insn & 0xc1c00000) == 0);
}
+/* Return non-zero if the instruction corresponding to PC is an
+ "annulled" branch, i.e. the annul bit is set. */
+
+int
+sparc_is_annulled_branch_insn (CORE_ADDR pc)
+{
+ /* The branch instructions featuring an annul bit can be identified
+ by the following bit patterns:
+
+ OP=0
+ OP2=1: Branch on Integer Condition Codes with Prediction (BPcc).
+ OP2=2: Branch on Integer Condition Codes (Bcc).
+ OP2=5: Branch on FP Condition Codes with Prediction (FBfcc).
+ OP2=6: Branch on FP Condition Codes (FBcc).
+ OP2=3 && Bit28=0:
+ Branch on Integer Register with Prediction (BPr).
+
+ This leaves out ILLTRAP (OP2=0), SETHI/NOP (OP2=4) and the V8
+ coprocessor branch instructions (Op2=7). */
+
+ const unsigned long insn = sparc_fetch_instruction (pc);
+ const unsigned op2 = X_OP2 (insn);
+
+ if ((X_OP (insn) == 0)
+ && ((op2 == 1) || (op2 == 2) || (op2 == 5) || (op2 == 6)
+ || ((op2 == 3) && ((insn & 0x10000000) == 0))))
+ return X_A (insn);
+ else
+ return 0;
+}
+
/* OpenBSD/sparc includes StackGhost, which according to the author's
website http://stackghost.cerias.purdue.edu "... transparently and
automatically protects applications' stack frames; more
regcache_raw_write (regcache, regnum + 1, buf + 4);
}
\f
+/* Implement "in_function_epilogue_p". */
+
+int
+sparc_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+ /* This function must return true if we are one instruction after an
+ instruction that destroyed the stack frame of the current
+ function. The SPARC instructions used to restore the callers
+ stack frame are RESTORE and RETURN/RETT.
+
+ Of these RETURN/RETT is a branch instruction and thus we return
+ true if we are in its delay slot.
+
+ RESTORE is almost always found in the delay slot of a branch
+ instruction that transfers control to the caller, such as JMPL.
+ Thus the next instruction is in the caller frame and we don't
+ need to do anything about it. */
+
+ unsigned int insn = sparc_fetch_instruction (pc - 4);
+
+ return X_RETTURN (insn);
+}
+\f
static CORE_ADDR
sparc32_frame_align (struct gdbarch *gdbarch, CORE_ADDR address)
sparc_alloc_frame_cache (void)
{
struct sparc_frame_cache *cache;
- int i;
cache = FRAME_OBSTACK_ZALLOC (struct sparc_frame_cache);
dynamic linker patches up the first PLT with some code that
starts with a SAVE instruction. Patch up PC such that it points
at the start of our PLT entry. */
- if (tdep->plt_entry_size > 0 && in_plt_section (current_pc, NULL))
+ if (tdep->plt_entry_size > 0 && in_plt_section (current_pc))
pc = current_pc - ((current_pc - pc) % tdep->plt_entry_size);
insn = sparc_fetch_instruction (pc);
}
static enum return_value_convention
-sparc32_return_value (struct gdbarch *gdbarch, struct type *func_type,
+sparc32_return_value (struct gdbarch *gdbarch, struct value *function,
struct type *type, struct regcache *regcache,
gdb_byte *readbuf, const gdb_byte *writebuf)
{
if (sparc_structure_or_union_p (type)
|| (sparc_floating_p (type) && TYPE_LENGTH (type) == 16))
{
+ ULONGEST sp;
+ CORE_ADDR addr;
+
if (readbuf)
{
- ULONGEST sp;
- CORE_ADDR addr;
-
regcache_cooked_read_unsigned (regcache, SPARC_SP_REGNUM, &sp);
addr = read_memory_unsigned_integer (sp + 64, 4, byte_order);
read_memory (addr, readbuf, TYPE_LENGTH (type));
}
+ if (writebuf)
+ {
+ regcache_cooked_read_unsigned (regcache, SPARC_SP_REGNUM, &sp);
+ addr = read_memory_unsigned_integer (sp + 64, 4, byte_order);
+ write_memory (addr, writebuf, TYPE_LENGTH (type));
+ }
return RETURN_VALUE_ABI_PRESERVES_ADDRESS;
}
{
unsigned long insn = sparc_fetch_instruction (pc);
int conditional_p = X_COND (insn) & 0x7;
- int branch_p = 0;
+ int branch_p = 0, fused_p = 0;
long offset = 0; /* Must be signed for sign-extend. */
- if (X_OP (insn) == 0 && X_OP2 (insn) == 3 && (insn & 0x1000000) == 0)
+ if (X_OP (insn) == 0 && X_OP2 (insn) == 3)
{
- /* Branch on Integer Register with Prediction (BPr). */
- branch_p = 1;
- conditional_p = 1;
+ if ((insn & 0x10000000) == 0)
+ {
+ /* Branch on Integer Register with Prediction (BPr). */
+ branch_p = 1;
+ conditional_p = 1;
+ }
+ else
+ {
+ /* Compare and Branch */
+ branch_p = 1;
+ fused_p = 1;
+ offset = 4 * X_DISP10 (insn);
+ }
}
else if (X_OP (insn) == 0 && X_OP2 (insn) == 6)
{
if (branch_p)
{
- if (conditional_p)
+ if (fused_p)
+ {
+ /* Fused compare-and-branch instructions are non-delayed,
+ and do not have an annuling capability. So we need to
+ always set a breakpoint on both the NPC and the branch
+ target address. */
+ gdb_assert (offset != 0);
+ return pc + offset;
+ }
+ else if (conditional_p)
{
/* For conditional branches, return nPC + 4 iff the annul
bit is 1. */
if (X_A (insn))
*npc = 0;
- gdb_assert (offset != 0);
return pc + offset;
}
}
}
\f
-/* Return the appropriate register set for the core section identified
- by SECT_NAME and SECT_SIZE. */
+/* Iterate over core file register note sections. */
-static const struct regset *
-sparc_regset_from_core_section (struct gdbarch *gdbarch,
- const char *sect_name, size_t sect_size)
+static void
+sparc_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- if (strcmp (sect_name, ".reg") == 0 && sect_size >= tdep->sizeof_gregset)
- return tdep->gregset;
-
- if (strcmp (sect_name, ".reg2") == 0 && sect_size >= tdep->sizeof_fpregset)
- return tdep->fpregset;
-
- return NULL;
+ cb (".reg", tdep->sizeof_gregset, tdep->gregset, NULL, cb_data);
+ cb (".reg2", tdep->sizeof_fpregset, tdep->fpregset, NULL, cb_data);
}
\f
return arches->gdbarch;
/* Allocate space for the new architecture. */
- tdep = XZALLOC (struct gdbarch_tdep);
+ tdep = XCNEW (struct gdbarch_tdep);
gdbarch = gdbarch_alloc (&info, tdep);
tdep->pc_regnum = SPARC32_PC_REGNUM;
/* If we have register sets, enable the generic core file support. */
if (tdep->gregset)
- set_gdbarch_regset_from_core_section (gdbarch,
- sparc_regset_from_core_section);
+ set_gdbarch_iterate_over_regset_sections
+ (gdbarch, sparc_iterate_over_regset_sections);
+
+ register_sparc_ravenscar_ops (gdbarch);
return gdbarch;
}
/* Helper functions for dealing with register sets. */
void
-sparc32_supply_gregset (const struct sparc_gregset *gregset,
+sparc32_supply_gregset (const struct sparc_gregmap *gregmap,
struct regcache *regcache,
int regnum, const void *gregs)
{
if (regnum == SPARC32_PSR_REGNUM || regnum == -1)
regcache_raw_supply (regcache, SPARC32_PSR_REGNUM,
- regs + gregset->r_psr_offset);
+ regs + gregmap->r_psr_offset);
if (regnum == SPARC32_PC_REGNUM || regnum == -1)
regcache_raw_supply (regcache, SPARC32_PC_REGNUM,
- regs + gregset->r_pc_offset);
+ regs + gregmap->r_pc_offset);
if (regnum == SPARC32_NPC_REGNUM || regnum == -1)
regcache_raw_supply (regcache, SPARC32_NPC_REGNUM,
- regs + gregset->r_npc_offset);
+ regs + gregmap->r_npc_offset);
if (regnum == SPARC32_Y_REGNUM || regnum == -1)
regcache_raw_supply (regcache, SPARC32_Y_REGNUM,
- regs + gregset->r_y_offset);
+ regs + gregmap->r_y_offset);
if (regnum == SPARC_G0_REGNUM || regnum == -1)
regcache_raw_supply (regcache, SPARC_G0_REGNUM, &zero);
if ((regnum >= SPARC_G1_REGNUM && regnum <= SPARC_O7_REGNUM) || regnum == -1)
{
- int offset = gregset->r_g1_offset;
+ int offset = gregmap->r_g1_offset;
for (i = SPARC_G1_REGNUM; i <= SPARC_O7_REGNUM; i++)
{
{
/* Not all of the register set variants include Locals and
Inputs. For those that don't, we read them off the stack. */
- if (gregset->r_l0_offset == -1)
+ if (gregmap->r_l0_offset == -1)
{
ULONGEST sp;
}
else
{
- int offset = gregset->r_l0_offset;
+ int offset = gregmap->r_l0_offset;
for (i = SPARC_L0_REGNUM; i <= SPARC_I7_REGNUM; i++)
{
}
void
-sparc32_collect_gregset (const struct sparc_gregset *gregset,
+sparc32_collect_gregset (const struct sparc_gregmap *gregmap,
const struct regcache *regcache,
int regnum, void *gregs)
{
if (regnum == SPARC32_PSR_REGNUM || regnum == -1)
regcache_raw_collect (regcache, SPARC32_PSR_REGNUM,
- regs + gregset->r_psr_offset);
+ regs + gregmap->r_psr_offset);
if (regnum == SPARC32_PC_REGNUM || regnum == -1)
regcache_raw_collect (regcache, SPARC32_PC_REGNUM,
- regs + gregset->r_pc_offset);
+ regs + gregmap->r_pc_offset);
if (regnum == SPARC32_NPC_REGNUM || regnum == -1)
regcache_raw_collect (regcache, SPARC32_NPC_REGNUM,
- regs + gregset->r_npc_offset);
+ regs + gregmap->r_npc_offset);
if (regnum == SPARC32_Y_REGNUM || regnum == -1)
regcache_raw_collect (regcache, SPARC32_Y_REGNUM,
- regs + gregset->r_y_offset);
+ regs + gregmap->r_y_offset);
if ((regnum >= SPARC_G1_REGNUM && regnum <= SPARC_O7_REGNUM) || regnum == -1)
{
- int offset = gregset->r_g1_offset;
+ int offset = gregmap->r_g1_offset;
/* %g0 is always zero. */
for (i = SPARC_G1_REGNUM; i <= SPARC_O7_REGNUM; i++)
{
/* Not all of the register set variants include Locals and
Inputs. For those that don't, we read them off the stack. */
- if (gregset->r_l0_offset != -1)
+ if (gregmap->r_l0_offset != -1)
{
- int offset = gregset->r_l0_offset;
+ int offset = gregmap->r_l0_offset;
for (i = SPARC_L0_REGNUM; i <= SPARC_I7_REGNUM; i++)
{
}
void
-sparc32_supply_fpregset (struct regcache *regcache,
+sparc32_supply_fpregset (const struct sparc_fpregmap *fpregmap,
+ struct regcache *regcache,
int regnum, const void *fpregs)
{
const gdb_byte *regs = fpregs;
for (i = 0; i < 32; i++)
{
if (regnum == (SPARC_F0_REGNUM + i) || regnum == -1)
- regcache_raw_supply (regcache, SPARC_F0_REGNUM + i, regs + (i * 4));
+ regcache_raw_supply (regcache, SPARC_F0_REGNUM + i,
+ regs + fpregmap->r_f0_offset + (i * 4));
}
if (regnum == SPARC32_FSR_REGNUM || regnum == -1)
- regcache_raw_supply (regcache, SPARC32_FSR_REGNUM, regs + (32 * 4) + 4);
+ regcache_raw_supply (regcache, SPARC32_FSR_REGNUM,
+ regs + fpregmap->r_fsr_offset);
}
void
-sparc32_collect_fpregset (const struct regcache *regcache,
+sparc32_collect_fpregset (const struct sparc_fpregmap *fpregmap,
+ const struct regcache *regcache,
int regnum, void *fpregs)
{
gdb_byte *regs = fpregs;
for (i = 0; i < 32; i++)
{
if (regnum == (SPARC_F0_REGNUM + i) || regnum == -1)
- regcache_raw_collect (regcache, SPARC_F0_REGNUM + i, regs + (i * 4));
+ regcache_raw_collect (regcache, SPARC_F0_REGNUM + i,
+ regs + fpregmap->r_f0_offset + (i * 4));
}
if (regnum == SPARC32_FSR_REGNUM || regnum == -1)
- regcache_raw_collect (regcache, SPARC32_FSR_REGNUM, regs + (32 * 4) + 4);
+ regcache_raw_collect (regcache, SPARC32_FSR_REGNUM,
+ regs + fpregmap->r_fsr_offset);
}
\f
/* SunOS 4. */
/* From <machine/reg.h>. */
-const struct sparc_gregset sparc32_sunos4_gregset =
+const struct sparc_gregmap sparc32_sunos4_gregmap =
{
0 * 4, /* %psr */
1 * 4, /* %pc */
4 * 4, /* %g1 */
-1 /* %l0 */
};
+
+const struct sparc_fpregmap sparc32_sunos4_fpregmap =
+{
+ 0 * 4, /* %f0 */
+ 33 * 4, /* %fsr */
+};
+
+const struct sparc_fpregmap sparc32_bsd_fpregmap =
+{
+ 0 * 4, /* %f0 */
+ 32 * 4, /* %fsr */
+};
\f
/* Provide a prototype to silence -Wmissing-prototypes. */