/* Target-dependent code for SPARC.
- Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- Free Software Foundation, Inc.
+ Copyright (C) 2003-2015 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
More information on StackGuard can be found on in:
- Mike Frantzen and Mike Shuey. "StackGhost: Hardware Facilitated
+ Mike Frantzen and Mike Shuey. "StackGhost: Hardware Facilitated
Stack Protection." 2001. Published in USENIX Security Symposium
'01. */
return 0;
}
+/* Check whether TYPE is "Complex Floating". */
+
+static int
+sparc_complex_floating_p (const struct type *type)
+{
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_COMPLEX:
+ {
+ int len = TYPE_LENGTH (type);
+ return (len == 8 || len == 16 || len == 32);
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
/* Check whether TYPE is "Structure or Union".
In terms of Ada subprogram calls, arrays are treated the same as
}
/* Return the GDB type object for the "standard" data type of data in
- register REGNUM. */
+ register REGNUM. */
static struct type *
sparc32_register_type (struct gdbarch *gdbarch, int regnum)
return builtin_type (gdbarch)->builtin_int32;
}
-static void
+static enum register_status
sparc32_pseudo_register_read (struct gdbarch *gdbarch,
struct regcache *regcache,
int regnum, gdb_byte *buf)
{
+ enum register_status status;
+
gdb_assert (regnum >= SPARC32_D0_REGNUM && regnum <= SPARC32_D30_REGNUM);
regnum = SPARC_F0_REGNUM + 2 * (regnum - SPARC32_D0_REGNUM);
- regcache_raw_read (regcache, regnum, buf);
- regcache_raw_read (regcache, regnum + 1, buf + 4);
+ status = regcache_raw_read (regcache, regnum, buf);
+ if (status == REG_VALID)
+ status = regcache_raw_read (regcache, regnum + 1, buf + 4);
+ return status;
}
static void
regcache_raw_write (regcache, regnum + 1, buf + 4);
}
\f
+/* Implement the stack_frame_destroyed_p gdbarch method. */
+
+int
+sparc_stack_frame_destroyed_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)
int len = TYPE_LENGTH (type);
if (sparc_structure_or_union_p (type)
- || (sparc_floating_p (type) && len == 16))
+ || (sparc_floating_p (type) && len == 16)
+ || sparc_complex_floating_p (type))
{
/* Structure, Union and Quad-Precision Arguments. */
sp -= len;
sparc_alloc_frame_cache (void)
{
struct sparc_frame_cache *cache;
- int i;
cache = FRAME_OBSTACK_ZALLOC (struct sparc_frame_cache);
/* Frameless until proven otherwise. */
cache->frameless_p = 1;
-
+ cache->frame_offset = 0;
+ cache->saved_regs_mask = 0;
+ cache->copied_regs_mask = 0;
cache->struct_return_p = 0;
return cache;
CORE_ADDR pc = start_pc;
unsigned long insn;
int offset_stack_checking_sequence = 0;
+ int probing_loop = 0;
/* With GCC, all stack checking sequences begin with the same two
- instructions. */
+ instructions, plus an optional one in the case of a probing loop:
+
+ sethi <some immediate>, %g1
+ sub %sp, %g1, %g1
+
+ or:
- /* sethi <some immediate>,%g1 */
+ sethi <some immediate>, %g1
+ sethi <some immediate>, %g4
+ sub %sp, %g1, %g1
+
+ or:
+
+ sethi <some immediate>, %g1
+ sub %sp, %g1, %g1
+ sethi <some immediate>, %g4
+
+ If the optional instruction is found (setting g4), assume that a
+ probing loop will follow. */
+
+ /* sethi <some immediate>, %g1 */
insn = sparc_fetch_instruction (pc);
pc = pc + 4;
if (!(X_OP (insn) == 0 && X_OP2 (insn) == 0x4 && X_RD (insn) == 1))
return start_pc;
- /* sub %sp, %g1, %g1 */
+ /* optional: sethi <some immediate>, %g4 */
insn = sparc_fetch_instruction (pc);
pc = pc + 4;
+ if (X_OP (insn) == 0 && X_OP2 (insn) == 0x4 && X_RD (insn) == 4)
+ {
+ probing_loop = 1;
+ insn = sparc_fetch_instruction (pc);
+ pc = pc + 4;
+ }
+
+ /* sub %sp, %g1, %g1 */
if (!(X_OP (insn) == 2 && X_OP3 (insn) == 0x4 && !X_I(insn)
&& X_RD (insn) == 1 && X_RS1 (insn) == 14 && X_RS2 (insn) == 1))
return start_pc;
insn = sparc_fetch_instruction (pc);
pc = pc + 4;
+ /* optional: sethi <some immediate>, %g4 */
+ if (X_OP (insn) == 0 && X_OP2 (insn) == 0x4 && X_RD (insn) == 4)
+ {
+ probing_loop = 1;
+ insn = sparc_fetch_instruction (pc);
+ pc = pc + 4;
+ }
+
/* First possible sequence:
[first two instructions above]
clr [%g1 - some immediate] */
}
/* Third sequence: A probing loop.
- [first two instructions above]
- sethi <some immediate>, %g4
+ [first three instructions above]
sub %g1, %g4, %g4
cmp %g1, %g4
be <disp>
add %g1, -<some immediate>, %g1
ba <disp>
clr [%g1]
+
+ And an optional last probe for the remainder:
+
clr [%g4 - some immediate] */
- /* sethi <some immediate>, %g4 */
- else if (X_OP (insn) == 0 && X_OP2 (insn) == 0x4 && X_RD (insn) == 4)
+ if (probing_loop)
{
/* sub %g1, %g4, %g4 */
- insn = sparc_fetch_instruction (pc);
- pc = pc + 4;
if (!(X_OP (insn) == 2 && X_OP3 (insn) == 0x4 && !X_I(insn)
&& X_RD (insn) == 4 && X_RS1 (insn) == 1 && X_RS2 (insn) == 4))
return start_pc;
if (!(X_OP (insn) == 0 && X_COND (insn) == 0x8))
return start_pc;
- /* clr [%g1] */
+ /* clr [%g1] (st %g0, [%g1] or st %g0, [%g1+0]) */
insn = sparc_fetch_instruction (pc);
pc = pc + 4;
- if (!(X_OP (insn) == 3 && X_OP3(insn) == 0x4 && !X_I(insn)
- && X_RD (insn) == 0 && X_RS1 (insn) == 1))
+ if (!(X_OP (insn) == 3 && X_OP3(insn) == 0x4
+ && X_RD (insn) == 0 && X_RS1 (insn) == 1
+ && (!X_I(insn) || X_SIMM13 (insn) == 0)))
return start_pc;
- /* clr [%g4 - some immediate] */
+ /* We found a valid stack-check sequence, return the new PC. */
+
+ /* optional: clr [%g4 - some immediate] */
insn = sparc_fetch_instruction (pc);
pc = pc + 4;
if (!(X_OP (insn) == 3 && X_OP3(insn) == 0x4 && X_I(insn)
&& X_RS1 (insn) == 4 && X_RD (insn) == 0))
- return start_pc;
-
- /* We found a valid stack-check sequence, return the new PC. */
- return pc;
+ return pc - 4;
+ else
+ return pc;
}
/* No stack check code in our prologue, return the start_pc. */
return start_pc;
}
+/* Record the effect of a SAVE instruction on CACHE. */
+
+void
+sparc_record_save_insn (struct sparc_frame_cache *cache)
+{
+ /* The frame is set up. */
+ cache->frameless_p = 0;
+
+ /* The frame pointer contains the CFA. */
+ cache->frame_offset = 0;
+
+ /* The `local' and `in' registers are all saved. */
+ cache->saved_regs_mask = 0xffff;
+
+ /* The `out' registers are all renamed. */
+ cache->copied_regs_mask = 0xff;
+}
+
+/* Do a full analysis of the prologue at PC and update CACHE accordingly.
+ Bail out early if CURRENT_PC is reached. Return the address where
+ the analysis stopped.
+
+ We handle both the traditional register window model and the single
+ register window (aka flat) model. */
+
CORE_ADDR
sparc_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR pc,
CORE_ADDR current_pc, struct sparc_frame_cache *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);
+ /* Recognize store insns and record their sources. */
+ while (X_OP (insn) == 3
+ && (X_OP3 (insn) == 0x4 /* stw */
+ || X_OP3 (insn) == 0x7 /* std */
+ || X_OP3 (insn) == 0xe) /* stx */
+ && X_RS1 (insn) == SPARC_SP_REGNUM)
+ {
+ int regnum = X_RD (insn);
+
+ /* Recognize stores into the corresponding stack slots. */
+ if (regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM
+ && ((X_I (insn)
+ && X_SIMM13 (insn) == (X_OP3 (insn) == 0xe
+ ? (regnum - SPARC_L0_REGNUM) * 8 + BIAS
+ : (regnum - SPARC_L0_REGNUM) * 4))
+ || (!X_I (insn) && regnum == SPARC_L0_REGNUM)))
+ {
+ cache->saved_regs_mask |= (1 << (regnum - SPARC_L0_REGNUM));
+ if (X_OP3 (insn) == 0x7)
+ cache->saved_regs_mask |= (1 << (regnum + 1 - SPARC_L0_REGNUM));
+ }
+
+ offset += 4;
+
+ insn = sparc_fetch_instruction (pc + offset);
+ }
+
/* Recognize a SETHI insn and record its destination. */
if (X_OP (insn) == 0 && X_OP2 (insn) == 0x04)
{
dest = X_RD (insn);
offset += 4;
- insn = sparc_fetch_instruction (pc + 4);
+ insn = sparc_fetch_instruction (pc + offset);
}
/* Allow for an arithmetic operation on DEST or %g1. */
{
offset += 4;
- insn = sparc_fetch_instruction (pc + 8);
+ insn = sparc_fetch_instruction (pc + offset);
}
/* Check for the SAVE instruction that sets up the frame. */
if (X_OP (insn) == 2 && X_OP3 (insn) == 0x3c)
{
- cache->frameless_p = 0;
- return pc + offset + 4;
+ sparc_record_save_insn (cache);
+ offset += 4;
+ return pc + offset;
+ }
+
+ /* Check for an arithmetic operation on %sp. */
+ if (X_OP (insn) == 2
+ && (X_OP3 (insn) == 0 || X_OP3 (insn) == 0x4)
+ && X_RS1 (insn) == SPARC_SP_REGNUM
+ && X_RD (insn) == SPARC_SP_REGNUM)
+ {
+ if (X_I (insn))
+ {
+ cache->frame_offset = X_SIMM13 (insn);
+ if (X_OP3 (insn) == 0)
+ cache->frame_offset = -cache->frame_offset;
+ }
+ offset += 4;
+
+ insn = sparc_fetch_instruction (pc + offset);
+
+ /* Check for an arithmetic operation that sets up the frame. */
+ if (X_OP (insn) == 2
+ && (X_OP3 (insn) == 0 || X_OP3 (insn) == 0x4)
+ && X_RS1 (insn) == SPARC_SP_REGNUM
+ && X_RD (insn) == SPARC_FP_REGNUM)
+ {
+ cache->frameless_p = 0;
+ cache->frame_offset = 0;
+ /* We could check that the amount subtracted to %sp above is the
+ same as the one added here, but this seems superfluous. */
+ cache->copied_regs_mask |= 0x40;
+ offset += 4;
+
+ insn = sparc_fetch_instruction (pc + offset);
+ }
+
+ /* Check for a move (or) operation that copies the return register. */
+ if (X_OP (insn) == 2
+ && X_OP3 (insn) == 0x2
+ && !X_I (insn)
+ && X_RS1 (insn) == SPARC_G0_REGNUM
+ && X_RS2 (insn) == SPARC_O7_REGNUM
+ && X_RD (insn) == SPARC_I7_REGNUM)
+ {
+ cache->copied_regs_mask |= 0x80;
+ offset += 4;
+ }
+
+ return pc + offset;
}
return pc;
indeed what GCC seems to be doing. In that case GCC will
generate debug information that points to the stack slots instead
of the registers, so we should consider the instructions that
- write out these incoming arguments onto the stack. Of course we
- only need to do this if we have a stack frame. */
+ write out these incoming arguments onto the stack. */
- while (!cache.frameless_p)
+ while (1)
{
unsigned long insn = sparc_fetch_instruction (start_pc);
- /* Recognize instructions that store incoming arguments in
- %i0...%i5 into the corresponding stack slot. */
- if (X_OP (insn) == 3 && (X_OP3 (insn) & 0x3c) == 0x04 && X_I (insn)
- && (X_RD (insn) >= 24 && X_RD (insn) <= 29) && X_RS1 (insn) == 30
- && X_SIMM13 (insn) == 68 + (X_RD (insn) - 24) * 4)
+ /* Recognize instructions that store incoming arguments into the
+ corresponding stack slots. */
+ if (X_OP (insn) == 3 && (X_OP3 (insn) & 0x3c) == 0x04
+ && X_I (insn) && X_RS1 (insn) == SPARC_FP_REGNUM)
{
- start_pc += 4;
- continue;
+ int regnum = X_RD (insn);
+
+ /* Case of arguments still in %o[0..5]. */
+ if (regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O5_REGNUM
+ && !(cache.copied_regs_mask & (1 << (regnum - SPARC_O0_REGNUM)))
+ && X_SIMM13 (insn) == 68 + (regnum - SPARC_O0_REGNUM) * 4)
+ {
+ start_pc += 4;
+ continue;
+ }
+
+ /* Case of arguments copied into %i[0..5]. */
+ if (regnum >= SPARC_I0_REGNUM && regnum <= SPARC_I5_REGNUM
+ && (cache.copied_regs_mask & (1 << (regnum - SPARC_I0_REGNUM)))
+ && X_SIMM13 (insn) == 68 + (regnum - SPARC_I0_REGNUM) * 4)
+ {
+ start_pc += 4;
+ continue;
+ }
}
break;
get_frame_register_unsigned (this_frame, SPARC_FP_REGNUM);
}
+ cache->base += cache->frame_offset;
+
if (cache->base & 1)
cache->base += BIAS;
an "unimp" instruction. If it is, then it is a struct-return
function. */
CORE_ADDR pc;
- int regnum = cache->frameless_p ? SPARC_O7_REGNUM : SPARC_I7_REGNUM;
+ int regnum =
+ (cache->copied_regs_mask & 0x80) ? SPARC_I7_REGNUM : SPARC_O7_REGNUM;
pc = get_frame_register_unsigned (this_frame, regnum) + 8;
if (sparc_is_unimp_insn (pc))
if (cache->struct_return_p)
pc += 4;
- regnum = cache->frameless_p ? SPARC_O7_REGNUM : SPARC_I7_REGNUM;
+ regnum =
+ (cache->copied_regs_mask & 0x80) ? SPARC_I7_REGNUM : SPARC_O7_REGNUM;
pc += get_frame_register_unsigned (this_frame, regnum) + 8;
return frame_unwind_got_constant (this_frame, regnum, pc);
}
}
}
- /* The previous frame's `local' and `in' registers have been saved
+ /* The previous frame's `local' and `in' registers may have been saved
in the register save area. */
- if (!cache->frameless_p
- && regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM)
+ if (regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM
+ && (cache->saved_regs_mask & (1 << (regnum - SPARC_L0_REGNUM))))
{
CORE_ADDR addr = cache->base + (regnum - SPARC_L0_REGNUM) * 4;
return frame_unwind_got_memory (this_frame, regnum, addr);
}
- /* The previous frame's `out' registers are accessible as the
- current frame's `in' registers. */
- if (!cache->frameless_p
- && regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM)
+ /* The previous frame's `out' registers may be accessible as the current
+ frame's `in' registers. */
+ if (regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM
+ && (cache->copied_regs_mask & (1 << (regnum - SPARC_O0_REGNUM))))
regnum += (SPARC_I0_REGNUM - SPARC_O0_REGNUM);
return frame_unwind_got_register (this_frame, regnum, regnum);
static const struct frame_unwind sparc32_frame_unwind =
{
NORMAL_FRAME,
+ default_frame_unwind_stop_reason,
sparc32_frame_this_id,
sparc32_frame_prev_register,
NULL,
gdb_byte *valbuf)
{
int len = TYPE_LENGTH (type);
- gdb_byte buf[8];
+ gdb_byte buf[32];
gdb_assert (!sparc_structure_or_union_p (type));
gdb_assert (!(sparc_floating_p (type) && len == 16));
- if (sparc_floating_p (type))
+ if (sparc_floating_p (type) || sparc_complex_floating_p (type))
{
/* Floating return values. */
regcache_cooked_read (regcache, SPARC_F0_REGNUM, buf);
if (len > 4)
regcache_cooked_read (regcache, SPARC_F1_REGNUM, buf + 4);
+ if (len > 8)
+ {
+ regcache_cooked_read (regcache, SPARC_F2_REGNUM, buf + 8);
+ regcache_cooked_read (regcache, SPARC_F3_REGNUM, buf + 12);
+ }
+ if (len > 16)
+ {
+ regcache_cooked_read (regcache, SPARC_F4_REGNUM, buf + 16);
+ regcache_cooked_read (regcache, SPARC_F5_REGNUM, buf + 20);
+ regcache_cooked_read (regcache, SPARC_F6_REGNUM, buf + 24);
+ regcache_cooked_read (regcache, SPARC_F7_REGNUM, buf + 28);
+ }
memcpy (valbuf, buf, len);
}
else
gdb_assert (!(sparc_floating_p (type) && len == 16));
gdb_assert (len <= 8);
- if (sparc_floating_p (type))
+ if (sparc_floating_p (type) || sparc_complex_floating_p (type))
{
/* Floating return values. */
memcpy (buf, valbuf, len);
regcache_cooked_write (regcache, SPARC_F0_REGNUM, buf);
if (len > 4)
regcache_cooked_write (regcache, SPARC_F1_REGNUM, buf + 4);
+ if (len > 8)
+ {
+ regcache_cooked_write (regcache, SPARC_F2_REGNUM, buf + 8);
+ regcache_cooked_write (regcache, SPARC_F3_REGNUM, buf + 12);
+ }
+ if (len > 16)
+ {
+ regcache_cooked_write (regcache, SPARC_F4_REGNUM, buf + 16);
+ regcache_cooked_write (regcache, SPARC_F5_REGNUM, buf + 20);
+ regcache_cooked_write (regcache, SPARC_F6_REGNUM, buf + 24);
+ regcache_cooked_write (regcache, SPARC_F7_REGNUM, buf + 28);
+ }
}
else
{
}
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;
}
sparc32_stabs_argument_has_addr (struct gdbarch *gdbarch, struct type *type)
{
return (sparc_structure_or_union_p (type)
- || (sparc_floating_p (type) && TYPE_LENGTH (type) == 16));
+ || (sparc_floating_p (type) && TYPE_LENGTH (type) == 16)
+ || sparc_complex_floating_p (type));
}
static int
{
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)
{
const gdb_byte *regs = gregs;
+ gdb_byte zero[4] = { 0 };
int i;
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, NULL);
+ 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. */