/* Target-machine dependent code for Nios II, for GDB.
- Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ Copyright (C) 2012-2015 Free Software Foundation, Inc.
Contributed by Peter Brookes (pbrookes@altera.com)
and Andrew Draper (adraper@altera.com).
Contributed by Mentor Graphics, Inc.
CORE_ADDR start_pc)
{
unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+ /* Maximum number of possibly-epilogue instructions to check.
+ Note that this number should not be too large, else we can
+ potentially end up iterating through unmapped memory. */
+ int ninsns, max_insns = 5;
unsigned int insn;
const struct nios2_opcode *op = NULL;
unsigned int uimm;
int imm;
int ra, rb, rc;
enum branch_condition cond;
+ CORE_ADDR pc;
/* There has to be a previous instruction in the function. */
- if (current_pc > start_pc)
- {
- int ok = 0;
+ if (current_pc <= start_pc)
+ return 0;
+
+ /* Find the previous instruction before current_pc.
+ For the moment we will assume that all instructions are the
+ same size here. */
+ pc = current_pc - NIOS2_OPCODE_SIZE;
- /* Check whether the previous instruction was a stack adjustment.
- Possible instructions here include:
+ /* Beginning with the previous instruction we just located, check whether
+ we are in a sequence of at least one stack adjustment instruction.
+ Possible instructions here include:
ADDI sp, sp, n
ADD sp, sp, rn
LDW sp, n(sp) */
- op = nios2_fetch_insn (gdbarch, current_pc - NIOS2_OPCODE_SIZE, &insn);
+ for (ninsns = 0; ninsns < max_insns; ninsns++)
+ {
+ int ok = 0;
+
+ /* Fetch the insn at pc. */
+ op = nios2_fetch_insn (gdbarch, pc, &insn);
if (op == NULL)
return 0;
+ pc += op->size;
/* Was it a stack adjustment? */
if (nios2_match_addi (insn, op, mach, &ra, &rb, &imm))
else if (nios2_match_ldw (insn, op, mach, &ra, &rb, &imm))
ok = (rb == NIOS2_SP_REGNUM);
if (!ok)
- return 0;
-
- /* Then check if it's followed by a return or a tail call. */
- op = nios2_fetch_insn (gdbarch, current_pc, &insn);
- if (op == NULL)
- return 0;
- if (nios2_match_jmpr (insn, op, mach, &ra)
- || nios2_match_jmpi (insn, op, mach, &uimm)
- || (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond)
- && cond == branch_none))
- return 1;
+ break;
}
+
+ /* No stack adjustments found. */
+ if (ninsns == 0)
+ return 0;
+
+ /* We found more stack adjustments than we expect GCC to be generating.
+ Since it looks like a stack unwind might be in progress tell GDB to
+ treat it as such. */
+ if (ninsns == max_insns)
+ return 1;
+
+ /* The next instruction following the stack adjustments must be a
+ return, jump, or unconditional branch. */
+ if (nios2_match_jmpr (insn, op, mach, &ra)
+ || nios2_match_jmpi (insn, op, mach, &uimm)
+ || (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond)
+ && cond == branch_none))
+ return 1;
+
return 0;
}
-/* Implement the in_function_epilogue_p gdbarch method. */
+/* Implement the stack_frame_destroyed_p gdbarch method. */
static int
-nios2_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+nios2_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc)
{
CORE_ADDR func_addr;
cache->reg_saved[NIOS2_SP_REGNUM].addr = -4;
}
+ else if (rc == NIOS2_SP_REGNUM && ra == NIOS2_FP_REGNUM)
+ /* This is setting SP from FP. This only happens in the
+ function epilogue. */
+ break;
+
else if (rc != 0)
{
if (value[rb].reg == 0)
value[rc].offset = value[ra].offset + value[rb].offset;
}
- prologue_end = pc;
+ /* The add/move is only considered a prologue instruction
+ if the destination is SP or FP. */
+ if (rc == NIOS2_SP_REGNUM || rc == NIOS2_FP_REGNUM)
+ prologue_end = pc;
}
else if (nios2_match_sub (insn, op, mach, &ra, &rb, &rc))
{
/* SUB rc, ra, rb */
- if (rc != 0)
+ if (rc == NIOS2_SP_REGNUM && rb == NIOS2_SP_REGNUM
+ && value[rc].reg != 0)
+ /* If we are decrementing the SP by a non-constant amount,
+ this is alloca, not part of the prologue. */
+ break;
+ else if (rc != 0)
{
if (value[rb].reg == 0)
value[rc].reg = value[ra].reg;
{
/* ADDI rb, ra, imm */
- /* The first stack adjustment is part of the prologue.
- Any subsequent stack adjustments are either down to
- alloca or the epilogue so stop analysing when we hit
- them. */
+ /* A positive stack adjustment has to be part of the epilogue. */
if (rb == NIOS2_SP_REGNUM
- && (value[rb].offset != 0 || value[ra].reg != NIOS2_SP_REGNUM))
+ && (imm > 0 || value[ra].reg != NIOS2_SP_REGNUM))
+ break;
+
+ /* Likewise restoring SP from FP. */
+ else if (rb == NIOS2_SP_REGNUM && ra == NIOS2_FP_REGNUM)
break;
if (rb != 0)
value[rb].offset = value[ra].offset + imm;
}
- prologue_end = pc;
+ /* The add is only considered a prologue instruction
+ if the destination is SP or FP. */
+ if (rb == NIOS2_SP_REGNUM || rb == NIOS2_FP_REGNUM)
+ prologue_end = pc;
}
else if (nios2_match_orhi (insn, op, mach, &ra, &rb, &uimm))
return nios2_analyze_prologue (gdbarch, start_pc, start_pc, &cache, NULL);
}
-/* Implement the breakpoint_from_pc gdbarch hook. */
+/* Implement the breakpoint_from_pc gdbarch hook.
+
+ The Nios II ABI for Linux says: "Userspace programs should not use
+ the break instruction and userspace debuggers should not insert
+ one." and "Userspace breakpoints are accomplished using the trap
+ instruction with immediate operand 31 (all ones)."
+
+ So, we use "trap 31" consistently as the breakpoint on bare-metal
+ as well as Linux targets. */
static const gdb_byte*
nios2_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *bp_addr,
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
- /* R1 break encoding:
- ((0x1e << 17) | (0x34 << 11) | (0x1f << 6) | (0x3a << 0))
- 0x003da7fa */
- static const gdb_byte r1_breakpoint_le[] = {0xfa, 0xa7, 0x3d, 0x0};
- static const gdb_byte r1_breakpoint_be[] = {0x0, 0x3d, 0xa7, 0xfa};
+ /* R1 trap encoding:
+ ((0x1d << 17) | (0x2d << 11) | (0x1f << 6) | (0x3a << 0))
+ 0x003b6ffa */
+ static const gdb_byte r1_breakpoint_le[] = {0xfa, 0x6f, 0x3b, 0x0};
+ static const gdb_byte r1_breakpoint_be[] = {0x0, 0x3b, 0x6f, 0xfa};
*bp_size = NIOS2_OPCODE_SIZE;
if (byte_order_for_code == BFD_ENDIAN_BIG)
return r1_breakpoint_be;
set_gdbarch_return_value (gdbarch, nios2_return_value);
set_gdbarch_skip_prologue (gdbarch, nios2_skip_prologue);
- set_gdbarch_in_function_epilogue_p (gdbarch, nios2_in_function_epilogue_p);
+ set_gdbarch_stack_frame_destroyed_p (gdbarch, nios2_stack_frame_destroyed_p);
set_gdbarch_breakpoint_from_pc (gdbarch, nios2_breakpoint_from_pc);
set_gdbarch_dummy_id (gdbarch, nios2_dummy_id);