aboutsummaryrefslogtreecommitdiff
path: root/gdb/arc-tdep.c
diff options
context:
space:
mode:
authorAnton Kolesov <Anton.Kolesov@synopsys.com>2017-02-10 14:12:09 +0300
committerAnton Kolesov <Anton.Kolesov@synopsys.com>2017-03-28 21:38:32 +0300
commitfe5f7374bef8f23ffa0fe0dee0f9b05e0a218a29 (patch)
tree40198df426b808a11314c42fa6754c09681a8062 /gdb/arc-tdep.c
parenteea787570f708e51048f812808e6cbd76fde6919 (diff)
downloadgdb-fe5f7374bef8f23ffa0fe0dee0f9b05e0a218a29.zip
gdb-fe5f7374bef8f23ffa0fe0dee0f9b05e0a218a29.tar.gz
gdb-fe5f7374bef8f23ffa0fe0dee0f9b05e0a218a29.tar.bz2
arc: Add prologue analysis
Add a prologue analysis that recognizes all instructions that may happen in compiler-generated prologue, including various stores, core register moves, subtraction and ENTER_S instruction that does a lot of prologue actions through microcode. Testcases cover various prologue scenarios, including instructions that are spread across multiple 16-bit encodings (for example there are 7 encodings of store instruction). gdb/ChangeLog: yyyy-mm-dd Anton Kolesov <anton.kolesov@synopsys.com> * arc-tdep.c (arc_frame_cache): Add support for prologue analysis. (arc_skip_prologue): Likewise. (arc_make_frame_cache): Likewise. (arc_pv_get_operand): New function. (arc_is_in_prologue): Likewise. (arc_analyze_prologue): Likewise. (arc_print_frame_cache): Likewise. (MAX_PROLOGUE_LENGTH): New constant. gdb/doc/ChangeLog: yyyy-mm-dd Anton Kolesov <anton.kolesov@synopsys.com> * gdb.texinfo (Synopsys ARC): Document "set debug arc 2". gdb/testsuite/ChangeLog: yyyy-mm-dd Anton Kolesov <anton.kolesov@synopsys.com> * gdb.arch/arc-analyze-prologue.S: New file. * gdb.arch/arc-analyze-prologue.exp: Likewise.
Diffstat (limited to 'gdb/arc-tdep.c')
-rw-r--r--gdb/arc-tdep.c498
1 files changed, 478 insertions, 20 deletions
diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index 69ac641..657b1eb 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -28,6 +28,7 @@
#include "gdbcore.h"
#include "gdbcmd.h"
#include "objfiles.h"
+#include "prologue-value.h"
#include "trad-frame.h"
/* ARC header files. */
@@ -42,8 +43,7 @@
#include "features/arc-v2.c"
#include "features/arc-arcompact.c"
-/* The frame unwind cache for the ARC. Current structure is a stub, because
- it should be filled in during the prologue analysis. */
+/* The frame unwind cache for ARC. */
struct arc_frame_cache
{
@@ -52,7 +52,35 @@ struct arc_frame_cache
frame. */
CORE_ADDR prev_sp;
- /* Store addresses for registers saved in prologue. */
+ /* Register that is a base for this frame - FP for normal frame, SP for
+ non-FP frames. */
+ int frame_base_reg;
+
+ /* Offset from the previous SP to the current frame base. If GCC uses
+ `SUB SP,SP,offset` to allocate space for local variables, then it will be
+ done after setting up a frame pointer, but it still will be considered
+ part of prologue, therefore SP will be lesser than FP at the end of the
+ prologue analysis. In this case that would be an offset from old SP to a
+ new FP. But in case of non-FP frames, frame base is an SP and thus that
+ would be an offset from old SP to new SP. What is important is that this
+ is an offset from old SP to a known register, so it can be used to find
+ old SP.
+
+ Using FP is preferable, when possible, because SP can change in function
+ body after prologue due to alloca, variadic arguments or other shenanigans.
+ If that is the case in the caller frame, then PREV_SP will point to SP at
+ the moment of function call, but it will be different from SP value at the
+ end of the caller prologue. As a result it will not be possible to
+ reconstruct caller's frame and go past it in the backtrace. Those things
+ are unlikely to happen to FP - FP value at the moment of function call (as
+ stored on stack in callee prologue) is also an FP value at the end of the
+ caller's prologue. */
+
+ LONGEST frame_base_offset;
+
+ /* Store addresses for registers saved in prologue. During prologue analysis
+ GDB stores offsets relatively to "old SP", then after old SP is evaluated,
+ offsets are replaced with absolute addresses. */
struct trad_frame_saved_reg *saved_regs;
};
@@ -117,6 +145,10 @@ static const char *const core_arcompact_register_names[] = {
"lp_count", "reserved", "limm", "pcl",
};
+/* Functions are sorted in the order as they are used in the
+ _initialize_arc_tdep (), which uses the same order as gdbarch.h. Static
+ functions are defined before the first invocation. */
+
/* Returns an unsigned value of OPERAND_NUM in instruction INSN.
For relative branch instructions returned value is an offset, not an actual
branch target. */
@@ -227,10 +259,6 @@ arc_insn_get_memory_offset (const struct arc_instruction &insn)
return value;
}
-/* Functions are sorted in the order as they are used in the
- _initialize_arc_tdep (), which uses the same order as gdbarch.h. Static
- functions are defined before the first invocation. */
-
CORE_ADDR
arc_insn_get_branch_target (const struct arc_instruction &insn)
{
@@ -913,6 +941,247 @@ arc_frame_base_address (struct frame_info *this_frame, void **prologue_cache)
return (CORE_ADDR) get_frame_register_unsigned (this_frame, ARC_FP_REGNUM);
}
+/* Helper function that returns valid pv_t for an instruction operand:
+ either a register or a constant. */
+
+static pv_t
+arc_pv_get_operand (pv_t *regs, const struct arc_instruction &insn, int operand)
+{
+ if (insn.operands[operand].kind == ARC_OPERAND_KIND_REG)
+ return regs[insn.operands[operand].value];
+ else
+ return pv_constant (arc_insn_get_operand_value (insn, operand));
+}
+
+/* Determine whether the given disassembled instruction may be part of a
+ function prologue. If it is, the information in the frame unwind cache will
+ be updated. */
+
+static bool
+arc_is_in_prologue (struct gdbarch *gdbarch, const struct arc_instruction &insn,
+ pv_t *regs, struct pv_area *stack)
+{
+ /* It might be that currently analyzed address doesn't contain an
+ instruction, hence INSN is not valid. It likely means that address points
+ to a data, non-initialized memory, or middle of a 32-bit instruction. In
+ practice this may happen if GDB connects to a remote target that has
+ non-zeroed memory. GDB would read PC value and would try to analyze
+ prologue, but there is no guarantee that memory contents at the address
+ specified in PC is address is a valid instruction. There is not much that
+ that can be done about that. */
+ if (!insn.valid)
+ return false;
+
+ /* Branch/jump or a predicated instruction. */
+ if (insn.is_control_flow || insn.condition_code != ARC_CC_AL)
+ return false;
+
+ /* Store of some register. May or may not update base address register. */
+ if (insn.insn_class == STORE || insn.insn_class == PUSH)
+ {
+ /* There is definetely at least one operand - register/value being
+ stored. */
+ gdb_assert (insn.operands_count > 0);
+
+ /* Store at some constant address. */
+ if (insn.operands_count > 1
+ && insn.operands[1].kind != ARC_OPERAND_KIND_REG)
+ return false;
+
+ /* Writeback modes:
+ Mode Address used Writeback value
+ --------------------------------------------------
+ No reg + offset no
+ A/AW reg + offset reg + offset
+ AB reg reg + offset
+ AS reg + (offset << scaling) no
+
+ "PUSH reg" is an alias to "ST.AW reg, [SP, -4]" encoding. However
+ 16-bit PUSH_S is a distinct instruction encoding, where offset and
+ base register are implied through opcode. */
+
+ /* Register with base memory address. */
+ int base_reg = arc_insn_get_memory_base_reg (insn);
+
+ /* Address where to write. arc_insn_get_memory_offset returns scaled
+ value for ARC_WRITEBACK_AS. */
+ pv_t addr;
+ if (insn.writeback_mode == ARC_WRITEBACK_AB)
+ addr = regs[base_reg];
+ else
+ addr = pv_add_constant (regs[base_reg],
+ arc_insn_get_memory_offset (insn));
+
+ if (pv_area_store_would_trash (stack, addr))
+ return false;
+
+ if (insn.data_size_mode != ARC_SCALING_D)
+ {
+ /* Find the value being stored. */
+ pv_t store_value = arc_pv_get_operand (regs, insn, 0);
+
+ /* What is the size of a the stored value? */
+ CORE_ADDR size;
+ if (insn.data_size_mode == ARC_SCALING_B)
+ size = 1;
+ else if (insn.data_size_mode == ARC_SCALING_H)
+ size = 2;
+ else
+ size = ARC_REGISTER_SIZE;
+
+ pv_area_store (stack, addr, size, store_value);
+ }
+ else
+ {
+ if (insn.operands[0].kind == ARC_OPERAND_KIND_REG)
+ {
+ /* If this is a double store, than write N+1 register as well. */
+ pv_t store_value1 = regs[insn.operands[0].value];
+ pv_t store_value2 = regs[insn.operands[0].value + 1];
+ pv_area_store (stack, addr, ARC_REGISTER_SIZE, store_value1);
+ pv_area_store (stack,
+ pv_add_constant (addr, ARC_REGISTER_SIZE),
+ ARC_REGISTER_SIZE, store_value2);
+ }
+ else
+ {
+ pv_t store_value
+ = pv_constant (arc_insn_get_operand_value (insn, 0));
+ pv_area_store (stack, addr, ARC_REGISTER_SIZE * 2, store_value);
+ }
+ }
+
+ /* Is base register updated? */
+ if (insn.writeback_mode == ARC_WRITEBACK_A
+ || insn.writeback_mode == ARC_WRITEBACK_AB)
+ regs[base_reg] = pv_add_constant (regs[base_reg],
+ arc_insn_get_memory_offset (insn));
+
+ return true;
+ }
+ else if (insn.insn_class == MOVE)
+ {
+ gdb_assert (insn.operands_count == 2);
+
+ /* Destination argument can be "0", so nothing will happen. */
+ if (insn.operands[0].kind == ARC_OPERAND_KIND_REG)
+ {
+ int dst_regnum = insn.operands[0].value;
+ regs[dst_regnum] = arc_pv_get_operand (regs, insn, 1);
+ }
+ return true;
+ }
+ else if (insn.insn_class == SUB)
+ {
+ gdb_assert (insn.operands_count == 3);
+
+ /* SUB 0,b,c. */
+ if (insn.operands[0].kind != ARC_OPERAND_KIND_REG)
+ return true;
+
+ int dst_regnum = insn.operands[0].value;
+ regs[dst_regnum] = pv_subtract (arc_pv_get_operand (regs, insn, 1),
+ arc_pv_get_operand (regs, insn, 2));
+ return true;
+ }
+ else if (insn.insn_class == ENTER)
+ {
+ /* ENTER_S is a prologue-in-instruction - it saves all callee-saved
+ registers according to given arguments thus greatly reducing code
+ size. Which registers will be actually saved depends on arguments.
+
+ ENTER_S {R13-...,FP,BLINK} stores registers in following order:
+
+ new SP ->
+ BLINK
+ R13
+ R14
+ R15
+ ...
+ FP
+ old SP ->
+
+ There are up to three arguments for this opcode, as presented by ARC
+ disassembler:
+ 1) amount of general-purpose registers to be saved - this argument is
+ always present even when it is 0;
+ 2) FP register number (27) if FP has to be stored, otherwise argument
+ is not present;
+ 3) BLINK register number (31) if BLINK has to be stored, otherwise
+ argument is not present. If both FP and BLINK are stored, then FP
+ is present before BLINK in argument list. */
+ gdb_assert (insn.operands_count > 0);
+
+ int regs_saved = arc_insn_get_operand_value (insn, 0);
+
+ bool is_fp_saved;
+ if (insn.operands_count > 1)
+ is_fp_saved = (insn.operands[1].value == ARC_FP_REGNUM);
+ else
+ is_fp_saved = false;
+
+ bool is_blink_saved;
+ if (insn.operands_count > 1)
+ is_blink_saved = (insn.operands[insn.operands_count - 1].value
+ == ARC_BLINK_REGNUM);
+ else
+ is_blink_saved = false;
+
+ /* Amount of bytes to be allocated to store specified registers. */
+ CORE_ADDR st_size = ((regs_saved + is_fp_saved + is_blink_saved)
+ * ARC_REGISTER_SIZE);
+ pv_t new_sp = pv_add_constant (regs[ARC_SP_REGNUM], -st_size);
+
+ /* Assume that if the last register (closest to new SP) can be written,
+ then it is possible to write all of them. */
+ if (pv_area_store_would_trash (stack, new_sp))
+ return false;
+
+ /* Current store address. */
+ pv_t addr = regs[ARC_SP_REGNUM];
+
+ if (is_fp_saved)
+ {
+ addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+ pv_area_store (stack, addr, ARC_REGISTER_SIZE, regs[ARC_FP_REGNUM]);
+ }
+
+ /* Registers are stored in backward order: from GP (R26) to R13. */
+ for (int i = ARC_R13_REGNUM + regs_saved - 1; i >= ARC_R13_REGNUM; i--)
+ {
+ addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+ pv_area_store (stack, addr, ARC_REGISTER_SIZE, regs[i]);
+ }
+
+ if (is_blink_saved)
+ {
+ addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+ pv_area_store (stack, addr, ARC_REGISTER_SIZE,
+ regs[ARC_BLINK_REGNUM]);
+ }
+
+ gdb_assert (pv_is_identical (addr, new_sp));
+
+ regs[ARC_SP_REGNUM] = new_sp;
+
+ if (is_fp_saved)
+ regs[ARC_FP_REGNUM] = regs[ARC_SP_REGNUM];
+
+ return true;
+ }
+
+ /* Some other architectures, like nds32 or arm, try to continue as far as
+ possible when building a prologue cache (as opposed to when skipping
+ prologue), so that cache will be as full as possible. However current
+ code for ARC doesn't recognize some instructions that may modify SP, like
+ ADD, AND, OR, etc, hence there is no way to guarantee that SP wasn't
+ clobbered by the skipped instruction. Potential existence of extension
+ instruction, which may do anything they want makes this even more complex,
+ so it is just better to halt on a first unrecognized instruction. */
+
+ return false;
+}
+
/* Copy of gdb_buffered_insn_length_fprintf from disasm.c. */
static int ATTRIBUTE_PRINTF (2, 3)
@@ -937,6 +1206,146 @@ arc_disassemble_info (struct gdbarch *gdbarch)
return di;
}
+/* Analyze the prologue and update the corresponding frame cache for the frame
+ unwinder for unwinding frames that doesn't have debug info. In such
+ situation GDB attempts to parse instructions in the prologue to understand
+ where each register is saved.
+
+ If CACHE is not NULL, then it will be filled with information about saved
+ registers.
+
+ There are several variations of prologue which GDB may encouter. "Full"
+ prologue looks like this:
+
+ sub sp,sp,<imm> ; Space for variadic arguments.
+ push blink ; Store return address.
+ push r13 ; Store callee saved registers (up to R26/GP).
+ push r14
+ push fp ; Store frame pointer.
+ mov fp,sp ; Update frame pointer.
+ sub sp,sp,<imm> ; Create space for local vars on the stack.
+
+ Depending on compiler options lots of things may change:
+
+ 1) BLINK is not saved in leaf functions.
+ 2) Frame pointer is not saved and updated if -fomit-frame-pointer is used.
+ 3) 16-bit versions of those instructions may be used.
+ 4) Instead of a sequence of several push'es, compiler may instead prefer to
+ do one subtract on stack pointer and then store registers using normal
+ store, that doesn't update SP. Like this:
+
+
+ sub sp,sp,8 ; Create space for calee-saved registers.
+ st r13,[sp,4] ; Store callee saved registers (up to R26/GP).
+ st r14,[sp,0]
+
+ 5) ENTER_S instruction can encode most of prologue sequence in one
+ instruction (except for those subtracts for variadic arguments and local
+ variables).
+ 6) GCC may use "millicode" functions from libgcc to store callee-saved
+ registers with minimal code-size requirements. This function currently
+ doesn't support this.
+
+ ENTRYPOINT is a function entry point where prologue starts.
+
+ LIMIT_PC is a maximum possible end address of prologue (meaning address
+ of first instruction after the prologue). It might also point to the middle
+ of prologue if execution has been stopped by the breakpoint at this address
+ - in this case debugger should analyze prologue only up to this address,
+ because further instructions haven't been executed yet.
+
+ Returns address of the first instruction after the prologue. */
+
+static CORE_ADDR
+arc_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR entrypoint,
+ const CORE_ADDR limit_pc, struct arc_frame_cache *cache)
+{
+ if (arc_debug)
+ debug_printf ("arc: analyze_prologue (entrypoint=%s, limit_pc=%s)\n",
+ paddress (gdbarch, entrypoint),
+ paddress (gdbarch, limit_pc));
+
+ /* Prologue values. Only core registers can be stored. */
+ pv_t regs[ARC_LAST_CORE_REGNUM + 1];
+ for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+ regs[i] = pv_register (i, 0);
+ struct pv_area *stack = make_pv_area (ARC_SP_REGNUM,
+ gdbarch_addr_bit (gdbarch));
+ struct cleanup *back_to = make_cleanup_free_pv_area (stack);
+
+ CORE_ADDR current_prologue_end = entrypoint;
+
+ /* Look at each instruction in the prologue. */
+ while (current_prologue_end < limit_pc)
+ {
+ struct arc_instruction insn;
+ struct disassemble_info di = arc_disassemble_info (gdbarch);
+ arc_insn_decode (current_prologue_end, &di, arc_delayed_print_insn,
+ &insn);
+
+ if (arc_debug >= 2)
+ arc_insn_dump (insn);
+
+ /* If this instruction is in the prologue, fields in the cache will be
+ updated, and the saved registers mask may be updated. */
+ if (!arc_is_in_prologue (gdbarch, insn, regs, stack))
+ {
+ /* Found an instruction that is not in the prologue. */
+ if (arc_debug)
+ debug_printf ("arc: End of prologue reached at address %s\n",
+ paddress (gdbarch, insn.address));
+ break;
+ }
+
+ current_prologue_end = arc_insn_get_linear_next_pc (insn);
+ }
+
+ if (cache != NULL)
+ {
+ /* Figure out if it is a frame pointer or just a stack pointer. */
+ if (pv_is_register (regs[ARC_FP_REGNUM], ARC_SP_REGNUM))
+ {
+ cache->frame_base_reg = ARC_FP_REGNUM;
+ cache->frame_base_offset = -regs[ARC_FP_REGNUM].k;
+ }
+ else
+ {
+ cache->frame_base_reg = ARC_SP_REGNUM;
+ cache->frame_base_offset = -regs[ARC_SP_REGNUM].k;
+ }
+
+ /* Assign offset from old SP to all saved registers. */
+ for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+ {
+ CORE_ADDR offset;
+ if (pv_area_find_reg (stack, gdbarch, i, &offset))
+ cache->saved_regs[i].addr = offset;
+ }
+ }
+
+ do_cleanups (back_to);
+ return current_prologue_end;
+}
+
+/* Estimated maximum prologue length in bytes. This should include:
+ 1) Store instruction for each callee-saved register (R25 - R13 + 1)
+ 2) Two instructions for FP
+ 3) One for BLINK
+ 4) Three substract instructions for SP (for variadic args, for
+ callee saved regs and for local vars) and assuming that those SUB use
+ long-immediate (hence double length).
+ 5) Stores of arguments registers are considered part of prologue too
+ (R7 - R1 + 1).
+ This is quite an extreme case, because even with -O0 GCC will collapse first
+ two SUBs into one and long immediate values are quite unlikely to appear in
+ this case, but still better to overshoot a bit - prologue analysis will
+ anyway stop at the first instruction that doesn't fit prologue, so this
+ limit will be rarely reached. */
+
+const static int MAX_PROLOGUE_LENGTH
+ = 4 * (ARC_R25_REGNUM - ARC_R13_REGNUM + 1 + 2 + 1 + 6
+ + ARC_LAST_ARG_REGNUM - ARC_FIRST_ARG_REGNUM + 1);
+
/* Implement the "skip_prologue" gdbarch method.
Skip the prologue for the function at PC. This is done by checking from
@@ -966,15 +1375,19 @@ arc_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
/* No prologue info in symbol table, have to analyze prologue. */
/* Find an upper limit on the function prologue using the debug
- information. If the debug information could not be used to provide that
- bound, then pass 0 and arc_scan_prologue will estimate value itself. */
+ information. If there is no debug information about prologue end, then
+ skip_prologue_using_sal will return 0. */
CORE_ADDR limit_pc = skip_prologue_using_sal (gdbarch, pc);
- /* We don't have a proper analyze_prologue function yet, but its result
- should be returned here. Currently GDB will just stop at the first
- instruction of function if debug information doesn't have prologue info;
- and if there is a debug info about prologue - this code path will not be
- taken at all. */
- return (limit_pc == 0 ? pc : limit_pc);
+
+ /* If there is no debug information at all, it is required to give some
+ semi-arbitrary hard limit on amount of bytes to scan during prologue
+ analysis. */
+ if (limit_pc == 0)
+ limit_pc = pc + MAX_PROLOGUE_LENGTH;
+
+ /* Find the address of the first instruction after the prologue by scanning
+ through it - no other information is needed, so pass NULL as a cache. */
+ return arc_analyze_prologue (gdbarch, pc, limit_pc, NULL);
}
/* Implement the "print_insn" gdbarch method.
@@ -1114,6 +1527,28 @@ arc_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
return align_down (sp, 4);
}
+/* Dump the frame info. Used for internal debugging only. */
+
+static void
+arc_print_frame_cache (struct gdbarch *gdbarch, char *message,
+ struct arc_frame_cache *cache, int addresses_known)
+{
+ debug_printf ("arc: frame_info %s\n", message);
+ debug_printf ("arc: prev_sp = %s\n", paddress (gdbarch, cache->prev_sp));
+ debug_printf ("arc: frame_base_reg = %i\n", cache->frame_base_reg);
+ debug_printf ("arc: frame_base_offset = %s\n",
+ plongest (cache->frame_base_offset));
+
+ for (int i = 0; i <= ARC_BLINK_REGNUM; i++)
+ {
+ if (trad_frame_addr_p (cache->saved_regs, i))
+ debug_printf ("arc: saved register %s at %s %s\n",
+ gdbarch_register_name (gdbarch, i),
+ (addresses_known) ? "address" : "offset",
+ paddress (gdbarch, cache->saved_regs[i].addr));
+ }
+}
+
/* Frame unwinder for normal frames. */
static struct arc_frame_cache *
@@ -1125,12 +1560,11 @@ arc_make_frame_cache (struct frame_info *this_frame)
struct gdbarch *gdbarch = get_frame_arch (this_frame);
CORE_ADDR block_addr = get_frame_address_in_block (this_frame);
- CORE_ADDR prev_pc = get_frame_pc (this_frame);
-
CORE_ADDR entrypoint, prologue_end;
if (find_pc_partial_function (block_addr, NULL, &entrypoint, &prologue_end))
{
struct symtab_and_line sal = find_pc_line (entrypoint, 0);
+ CORE_ADDR prev_pc = get_frame_pc (this_frame);
if (sal.line == 0)
/* No line info so use current PC. */
prologue_end = prev_pc;
@@ -1142,18 +1576,42 @@ arc_make_frame_cache (struct frame_info *this_frame)
}
else
{
+ /* If find_pc_partial_function returned nothing then there is no symbol
+ information at all for this PC. Currently it is assumed in this case
+ that current PC is entrypoint to function and try to construct the
+ frame from that. This is, probably, suboptimal, for example ARM
+ assumes in this case that program is inside the normal frame (with
+ frame pointer). ARC, perhaps, should try to do the same. */
entrypoint = get_frame_register_unsigned (this_frame,
gdbarch_pc_regnum (gdbarch));
- prologue_end = 0;
+ prologue_end = entrypoint + MAX_PROLOGUE_LENGTH;
}
/* Allocate new frame cache instance and space for saved register info.
- * FRAME_OBSTACK_ZALLOC will initialize fields to zeroes. */
+ FRAME_OBSTACK_ZALLOC will initialize fields to zeroes. */
struct arc_frame_cache *cache
= FRAME_OBSTACK_ZALLOC (struct arc_frame_cache);
cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
- /* Should call analyze_prologue here, when it will be implemented. */
+ arc_analyze_prologue (gdbarch, entrypoint, prologue_end, cache);
+
+ if (arc_debug)
+ arc_print_frame_cache (gdbarch, "after prologue", cache, false);
+
+ CORE_ADDR unwound_fb = get_frame_register_unsigned (this_frame,
+ cache->frame_base_reg);
+ if (unwound_fb == 0)
+ return cache;
+ cache->prev_sp = unwound_fb + cache->frame_base_offset;
+
+ for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+ {
+ if (trad_frame_addr_p (cache->saved_regs, i))
+ cache->saved_regs[i].addr += cache->prev_sp;
+ }
+
+ if (arc_debug)
+ arc_print_frame_cache (gdbarch, "after previous SP found", cache, true);
return cache;
}