/* Target-dependent code for SPARC.
- Copyright 2003, 2004 Free Software Foundation, Inc.
+ Copyright 2003, 2004, 2005 Free Software Foundation, Inc.
This file is part of GDB.
struct regset;
-/* This file implements the The SPARC 32-bit ABI as defined by the
- section "Low-Level System Information" of the SPARC Compliance
- Definition (SCD) 2.4.1, which is the 32-bit System V psABI for
- SPARC. The SCD lists changes with respect to the origional 32-bit
- psABI as defined in the "System V ABI, SPARC Processor
- Supplement".
+/* This file implements the SPARC 32-bit ABI as defined by the section
+ "Low-Level System Information" of the SPARC Compliance Definition
+ (SCD) 2.4.1, which is the 32-bit System V psABI for SPARC. The SCD
+ lists changes with respect to the original 32-bit psABI as defined
+ in the "System V ABI, SPARC Processor Supplement".
Note that if we talk about SunOS, we mean SunOS 4.x, which was
BSD-based, which is sometimes (retroactively?) referred to as
#define X_OP2(i) (((i) >> 22) & 0x7)
#define X_IMM22(i) ((i) & 0x3fffff)
#define X_OP3(i) (((i) >> 19) & 0x3f)
+#define X_RS1(i) (((i) >> 14) & 0x1f)
#define X_I(i) (((i) >> 13) & 1)
/* Sign extension macros. */
#define X_DISP22(i) ((X_IMM22 (i) ^ 0x200000) - 0x200000)
#define X_DISP19(i) ((((i) & 0x7ffff) ^ 0x40000) - 0x40000)
+#define X_SIMM13(i) ((((i) & 0x1fff) ^ 0x1000) - 0x1000)
/* Fetch the instruction at PC. Instructions are always big-endian
even if the processor operates in little-endian mode. */
return insn;
}
\f
+
+/* Return non-zero if the instruction corresponding to PC is an "unimp"
+ instruction. */
+
+static int
+sparc_is_unimp_insn (CORE_ADDR pc)
+{
+ const unsigned long insn = sparc_fetch_instruction (pc);
+
+ return ((insn & 0xc1c00000) == 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
+ specifically, it guards the return pointers. The protection
+ mechanisms require no application source or binary modification and
+ imposes only a negligible performance penalty."
+
+ The same website provides the following description of how
+ StackGhost works:
+
+ "StackGhost interfaces with the kernel trap handler that would
+ normally write out registers to the stack and the handler that
+ would read them back in. By XORing a cookie into the
+ return-address saved in the user stack when it is actually written
+ to the stack, and then XOR it out when the return-address is pulled
+ from the stack, StackGhost can cause attacker corrupted return
+ pointers to behave in a manner the attacker cannot predict.
+ StackGhost can also use several unused bits in the return pointer
+ to detect a smashed return pointer and abort the process."
+
+ For GDB this means that whenever we're reading %i7 from a stack
+ frame's window save area, we'll have to XOR the cookie.
+
+ More information on StackGuard can be found on in:
+
+ Mike Frantzen and Mike Shuey. "StackGhost: Hardware Facilitated
+ Stack Protection." 2001. Published in USENIX Security Symposium
+ '01. */
+
+/* Fetch StackGhost Per-Process XOR cookie. */
+
+ULONGEST
+sparc_fetch_wcookie (void)
+{
+ struct target_ops *ops = ¤t_target;
+ char buf[8];
+ int len;
+
+ len = target_read_partial (ops, TARGET_OBJECT_WCOOKIE, NULL, buf, 0, 8);
+ if (len == -1)
+ return 0;
+
+ /* We should have either an 32-bit or an 64-bit cookie. */
+ gdb_assert (len == 4 || len == 8);
+
+ return extract_unsigned_integer (buf, len);
+}
+\f
+
/* Return the contents if register REGNUM as an address. */
static CORE_ADDR
static int
sparc_integral_or_pointer_p (const struct type *type)
{
+ int len = TYPE_LENGTH (type);
+
switch (TYPE_CODE (type))
{
case TYPE_CODE_INT:
case TYPE_CODE_CHAR:
case TYPE_CODE_ENUM:
case TYPE_CODE_RANGE:
- {
- /* We have byte, half-word, word and extended-word/doubleword
- integral types. The doubleword is an extension to the
- origional 32-bit ABI by the SCD 2.4.x. */
- int len = TYPE_LENGTH (type);
- return (len == 1 || len == 2 || len == 4 || len == 8);
- }
- return 1;
+ /* We have byte, half-word, word and extended-word/doubleword
+ integral types. The doubleword is an extension to the
+ original 32-bit ABI by the SCD 2.4.x. */
+ return (len == 1 || len == 2 || len == 4 || len == 8);
case TYPE_CODE_PTR:
case TYPE_CODE_REF:
- {
- /* Allow either 32-bit or 64-bit pointers. */
- int len = TYPE_LENGTH (type);
- return (len == 4 || len == 8);
- }
- return 1;
+ /* Allow either 32-bit or 64-bit pointers. */
+ return (len == 4 || len == 8);
default:
break;
}
for (i = 0; i < nargs; i++)
{
- struct type *type = VALUE_TYPE (args[i]);
+ struct type *type = value_type (args[i]);
int len = TYPE_LENGTH (type);
if (sparc_structure_or_union_p (type)
correct, and wasting a few bytes shouldn't be a problem. */
sp &= ~0x7;
- write_memory (sp, VALUE_CONTENTS (args[i]), len);
+ write_memory (sp, value_contents (args[i]), len);
args[i] = value_from_pointer (lookup_pointer_type (type), sp);
num_elements++;
}
for (i = 0; i < nargs; i++)
{
- char *valbuf = VALUE_CONTENTS (args[i]);
- struct type *type = VALUE_TYPE (args[i]);
+ const bfd_byte *valbuf = value_contents (args[i]);
+ struct type *type = value_type (args[i]);
int len = TYPE_LENGTH (type);
gdb_assert (len == 4 || len == 8);
}
static CORE_ADDR
-sparc32_push_dummy_call (struct gdbarch *gdbarch, CORE_ADDR func_addr,
+sparc32_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)
return sal.end;
}
- return sparc_analyze_prologue (start_pc, 0xffffffffUL, &cache);
+ start_pc = sparc_analyze_prologue (start_pc, 0xffffffffUL, &cache);
+
+ /* The psABI says that "Although the first 6 words of arguments
+ reside in registers, the standard stack frame reserves space for
+ them.". It also suggests that a function may use that space to
+ "write incoming arguments 0 to 5" into that space, and that's
+ 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. */
+
+ while (!cache.frameless_p)
+ {
+ 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)
+ {
+ start_pc += 4;
+ continue;
+ }
+
+ break;
+ }
+
+ return start_pc;
}
/* Normal frames. */
cache = sparc_alloc_frame_cache ();
*this_cache = cache;
- /* In priciple, for normal frames, %fp (%i6) holds the frame
- pointer, which holds the base address for the current stack
- frame. */
-
- cache->base = frame_unwind_register_unsigned (next_frame, SPARC_FP_REGNUM);
- if (cache->base == 0)
- return cache;
-
cache->pc = frame_func_unwind (next_frame);
if (cache->pc != 0)
{
if (cache->frameless_p)
{
- /* We didn't find a valid frame, which means that CACHE->base
- currently holds the frame pointer for our calling frame. */
- cache->base = frame_unwind_register_unsigned (next_frame,
- SPARC_SP_REGNUM);
+ /* This function is frameless, so %fp (%i6) holds the frame
+ pointer for our calling frame. Use %sp (%o6) as this frame's
+ base address. */
+ cache->base =
+ frame_unwind_register_unsigned (next_frame, SPARC_SP_REGNUM);
+ }
+ else
+ {
+ /* For normal frames, %fp (%i6) holds the frame pointer, the
+ base address for the current stack frame. */
+ cache->base =
+ frame_unwind_register_unsigned (next_frame, SPARC_FP_REGNUM);
}
+ if (cache->base & 1)
+ cache->base += BIAS;
+
return cache;
}
cache->struct_return_p = 1;
}
}
+ else
+ {
+ /* There is no debugging information for this function to
+ help us determine whether this function returns a struct
+ or not. So we rely on another heuristic which is to check
+ the instruction at the return address and see if this is
+ 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;
+
+ pc = frame_unwind_register_unsigned (next_frame, regnum) + 8;
+ if (sparc_is_unimp_insn (pc))
+ cache->struct_return_p = 1;
+ }
return cache;
}
return;
}
+ /* Handle StackGhost. */
+ {
+ ULONGEST wcookie = sparc_fetch_wcookie ();
+
+ if (wcookie != 0 && !cache->frameless_p && regnum == SPARC_I7_REGNUM)
+ {
+ *optimizedp = 0;
+ *lvalp = not_lval;
+ *addrp = 0;
+ *realnump = -1;
+ if (valuep)
+ {
+ CORE_ADDR addr = cache->base + (regnum - SPARC_L0_REGNUM) * 4;
+ ULONGEST i7;
+
+ /* Read the value in from memory. */
+ i7 = get_frame_memory_unsigned (next_frame, addr, 4);
+ store_unsigned_integer (valuep, 4, i7 ^ wcookie);
+ }
+ return;
+ }
+ }
+
/* The previous frame's `local' and `in' registers have been saved
in the register save area. */
if (!cache->frameless_p
&& regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM)
regnum += (SPARC_I0_REGNUM - SPARC_O0_REGNUM);
- frame_register_unwind (next_frame, regnum,
- optimizedp, lvalp, addrp, realnump, valuep);
+ *optimizedp = 0;
+ *lvalp = lval_register;
+ *addrp = 0;
+ *realnump = regnum;
+ if (valuep)
+ frame_unwind_register (next_frame, (*realnump), valuep);
}
static const struct frame_unwind sparc32_frame_unwind =
CORE_ADDR sp;
sp = frame_unwind_register_unsigned (next_frame, SPARC_SP_REGNUM);
+ if (sp & 1)
+ sp += BIAS;
return frame_id_build (sp, frame_pc_unwind (next_frame));
}
\f
#if 0
/* NOTE: cagney/2004-01-17: For the moment disable this method. The
architecture and CORE-gdb will need new code (and a replacement for
- EXTRACT_STRUCT_VALUE_ADDRESS) before this can be made to work
- robustly. Here is a possible function signature: */
+ DEPRECATED_EXTRACT_STRUCT_VALUE_ADDRESS) before this can be made to
+ work robustly. Here is a possible function signature: */
/* NOTE: cagney/2004-01-17: So far only the 32-bit SPARC ABI has been
identifed as having a way to robustly recover the address of a
struct-convention return-value (after the function has returned).
if (insert_breakpoints_p)
{
- CORE_ADDR pc;
+ CORE_ADDR pc, orig_npc;
pc = sparc_address_from_register (tdep->pc_regnum);
- npc = sparc_address_from_register (tdep->npc_regnum);
+ orig_npc = npc = sparc_address_from_register (tdep->npc_regnum);
/* Analyze the instruction at PC. */
nnpc = sparc_analyze_control_transfer (pc, &npc);
target_insert_breakpoint (nnpc, nnpc_save);
/* Assert that we have set at least one breakpoint, and that
- they're not set at the same spot. */
- gdb_assert (npc != 0 || nnpc != 0);
- gdb_assert (nnpc != npc);
+ they're not set at the same spot - unless we're going
+ from here straight to NULL, i.e. a call or jump to 0. */
+ gdb_assert (npc != 0 || nnpc != 0 || orig_npc == 0);
+ gdb_assert (nnpc != npc || orig_npc == 0);
}
else
{
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- if (strcmp (sect_name, ".reg") == 0 && sect_size == tdep->sizeof_gregset)
+ 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)
+ if (strcmp (sect_name, ".reg2") == 0 && sect_size >= tdep->sizeof_fpregset)
return tdep->fpregset;
return NULL;
tdep->pc_regnum = SPARC32_PC_REGNUM;
tdep->npc_regnum = SPARC32_NPC_REGNUM;
tdep->gregset = NULL;
- tdep->sizeof_gregset = 20 * 4;
+ tdep->sizeof_gregset = 0;
tdep->fpregset = NULL;
- tdep->sizeof_fpregset = 33 * 4;
+ tdep->sizeof_fpregset = 0;
tdep->plt_entry_size = 0;
set_gdbarch_long_double_bit (gdbarch, 128);
if (regnum == i || regnum == -1)
{
target_read_memory (sp + ((i - SPARC_L0_REGNUM) * 8), buf, 8);
+
+ /* Handle StackGhost. */
+ if (i == SPARC_I7_REGNUM)
+ {
+ ULONGEST wcookie = sparc_fetch_wcookie ();
+ ULONGEST i7 = extract_unsigned_integer (buf + offset, 8);
+
+ store_unsigned_integer (buf + offset, 8, i7 ^ wcookie);
+ }
+
regcache_raw_supply (regcache, i, buf);
}
}
{
target_read_memory (sp + ((i - SPARC_L0_REGNUM) * 4),
buf + offset, 4);
+
+ /* Handle StackGhost. */
+ if (i == SPARC_I7_REGNUM)
+ {
+ ULONGEST wcookie = sparc_fetch_wcookie ();
+ ULONGEST i7 = extract_unsigned_integer (buf + offset, 4);
+
+ store_unsigned_integer (buf + offset, 4, i7 ^ wcookie);
+ }
+
regcache_raw_supply (regcache, i, buf);
}
}
if (regnum == -1 || regnum == SPARC_SP_REGNUM || regnum == i)
{
regcache_raw_collect (regcache, i, buf);
+
+ /* Handle StackGhost. */
+ if (i == SPARC_I7_REGNUM)
+ {
+ ULONGEST wcookie = sparc_fetch_wcookie ();
+ ULONGEST i7 = extract_unsigned_integer (buf + offset, 8);
+
+ store_unsigned_integer (buf, 8, i7 ^ wcookie);
+ }
+
target_write_memory (sp + ((i - SPARC_L0_REGNUM) * 8), buf, 8);
}
}
if (regnum == -1 || regnum == SPARC_SP_REGNUM || regnum == i)
{
regcache_raw_collect (regcache, i, buf);
+
+ /* Handle StackGhost. */
+ if (i == SPARC_I7_REGNUM)
+ {
+ ULONGEST wcookie = sparc_fetch_wcookie ();
+ ULONGEST i7 = extract_unsigned_integer (buf + offset, 4);
+
+ store_unsigned_integer (buf + offset, 4, i7 ^ wcookie);
+ }
+
target_write_memory (sp + ((i - SPARC_L0_REGNUM) * 4),
buf + offset, 4);
}