summaryrefslogtreecommitdiff
path: root/gdb/arc-tdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/arc-tdep.c')
-rw-r--r--gdb/arc-tdep.c3840
1 files changed, 2216 insertions, 1624 deletions
diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index 8b542579ad3..f912f896dec 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -1,1999 +1,2591 @@
-/* Target dependent code for ARC700, for GDB, the GNU debugger.
+/* Target dependent code for ARC processor family, for GDB, the GNU debugger.
- Copyright 2005 Free Software Foundation, Inc.
+ Copyright 2005, 2008, 2009 Free Software Foundation, Inc.
Contributed by Codito Technologies Pvt. Ltd. (www.codito.com)
- Authors:
- Soam Vasani <soam.vasani@codito.com>
- Ramana Radhakrishnan <ramana.radhakrishnan@codito.com>
+ Authors:
+ Soam Vasani <soam.vasani@codito.com>
+ Ramana Radhakrishnan <ramana.radhakrishnan@codito.com>
+ Richard Stuckey <richard.stuckey@arc.com>
This file is part of GDB.
-
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/******************************************************************************/
+/* */
+/* Outline: */
+/* This module provides support for the ARC processor family's target */
+/* dependencies. In particular, it has knowledge of the processor ABI. */
+/* */
+/* See */
+/* ARCompact Instruction Set Architecture */
+/* Programmer's Reference */
+/* (5115-018) */
+/* */
+/* for a description of ARC processor architecture, and */
+/* */
+/* System V ABI Supplement */
+/* 4093-004 */
+/* */
+/* for a complete definition of the ABI. */
+/* */
+/* */
+/* Stack Frame Layout: */
+/* This shows the layout of the stack frame for the general case of a */
+/* function call; a given function might not have a variable number of */
+/* arguments or local variables, or might not save any registers, so it */
+/* would not have the corresponding frame areas. Additionally, a leaf */
+/* function (i.e. one which calls no other functions) does not need to */
+/* save the contents of the BLINK register (which holds its return */
+/* address), and a function might not have a frame pointer. */
+/* */
+/* N.B. the stack grows downward, so SP points below FP in memory; SP */
+/* always points to the last used word on the stack, not the first */
+/* one. */
+/* */
+/* | | | */
+/* | arg word N | | caller's */
+/* | : | | frame */
+/* | arg word 10 | | */
+/* | arg word 9 | | */
+/* old SP ---> |-----------------------| -- */
+/* | var arg word 8 | | */
+/* | : | | */
+/* | var arg word P+1 | | */
+/* |-----------------------| | */
+/* | | | */
+/* | callee-saved | | */
+/* | registers | | */
+/* | | | */
+/* |-----------------------| | */
+/* | saved blink (*) | | */
+/* |-----------------------| | callee's */
+/* | saved FP | | frame */
+/* FP ---> |-----------------------| | */
+/* | | | */
+/* | local | | */
+/* | variables | | */
+/* | | | */
+/* | register | | */
+/* | spill area | | */
+/* | | | */
+/* | outgoing args | | */
+/* | | | */
+/* SP ---> |-----------------------| -- */
+/* | | */
+/* | unused | */
+/* | | */
+/* | */
+/* | */
+/* V */
+/* downwards */
+/* */
+/* The list of arguments to be passed to a function is considered to be a */
+/* sequence of N words (as though all the parameters were stored in order */
+/* in memory with each parameter occupying an integral number of words). */
+/* Words 1 .. 8 are passed in registers 0 .. 7; if the function has more */
+/* than 8 words of arguments then words 9 .. N are passed on the stack in */
+/* the caller's frame. */
+/* */
+/* If the function has a variable number of arguments, e.g. it has a form */
+/* such as */
+/* function(p1, p2, ...); */
+/* */
+/* and P words are required to hold the values of the named parameters */
+/* (which are passed in registers 0 .. P-1), then the remaining 8 - P */
+/* words passed in registers P .. 7 are spilled into the top of the frame */
+/* so that the anonymous parameter words occupy a continous region. */
+/* */
+/* (*) if saved. */
+/* */
+/* Usage: */
+/* The module exports a function _initialize_arc_tdep: the call to this */
+/* function is generated by the gdb build mechanism, so this function */
+/* should not be explicitly called. */
+/* */
+/* The operations provided by this module are registered with gdb during */
+/* initialization; gdb then calls them via function pointers, rather than */
+/* by name (this allows gdb to handle multiple target architectures): */
+/* */
+/* set_gdbarch_XXX (gdbarch, <function>); */
+/* */
+/* */
+/* Build Configuration: */
+/* The ARC gdb may be built in two different configurations, according to */
+/* the nature of the target that it is to debug: */
+/* */
+/* 1) arc-elf32: */
+/* for debugging 'bare-metal' builds of user code (i.e. built with */
+/* newlib) */
+/* */
+/* ARC-specific modules: */
+/* arc-tdep */
+/* arc-elf32-tdep */
+/* arc-xiss */
+/* arc-jtag */
+/* arc-jtag-ops */
+/* arc-jtag-actionpoints */
+/* arc-gpio */
+/* arc-remote-fileio */
+/* arc-registers */
+/* arc-architecture */
+/* arc-board */
+/* arc-arguments */
+/* arc-memory */
+/* arc-inst-tracing */
+/* */
+/* 2) arc-linux-uclibc: */
+/* for deugging user mode Linux applications, via communication to */
+/* the remote gdbserver process, running on Linux for ARC700 */
+/* */
+/* ARC-specific modules: */
+/* arc-tdep */
+/* arc-linux-tdep */
+/* */
+/* This module provides operations which are common to both; operations */
+/* which are specific to one, or which have different variants in each */
+/* configuration, are provided by the other modules. */
+/* */
+/* */
+/* Debug Targets: */
+/* The ARC gdb supports a number of debug targets. These are: */
+/* */
+/* arc-elf32-gdb */
+/* built-in simulator 'target sim' */
+/* ARCangel 4 h/w emulator 'target arcjtag' */
+/* dynamically loaded xISS simulator 'target arcxiss' */
+/* separately executing xISS simulator 'target remote' */
+/* */
+/* arc-linux-uclibc-gdb */
+/* gdbserver running on ARC Linux 'target remote' */
+/* */
+/* It should, in theory, be possible for either debugger to connect to */
+/* any remote target which supports the gdb Remote Serial Protocol. */
+/* However, any such target MUST agree with the debugger on the register */
+/* numbering scheme that is used, as this controls the order of register */
+/* contents held in the RSP 'G' (set all registers) packet and the 'g' */
+/* (get all registers) response packet, as well as the register numbers */
+/* used in the 'P' (set one register) and 'p' (get one register) packets, */
+/* and in the 'T' stop reply packet. The schemes used by each debugger */
+/* are defined in the arc-elf32-tdep and arc-linux-tdep modules. */
+/* */
+/******************************************************************************/
+
+/* system header files */
#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <byteswap.h>
+/* gdb header files */
#include "defs.h"
+#include "config.h"
#include "arch-utils.h"
#include "dis-asm.h"
-#include "gdbtypes.h"
#include "frame.h"
#include "frame-unwind.h"
-#include "target.h"
-#include "breakpoint.h"
#include "inferior.h"
#include "regcache.h"
#include "reggroups.h"
#include "trad-frame.h"
#include "dwarf2-frame.h"
-#include "gdbtypes.h"
-#include "gdb_assert.h"
#include "gdbcore.h"
#include "observer.h"
#include "osabi.h"
+#include "gdbcmd.h"
+#include "block.h"
+#include "dictionary.h"
+#include "language.h"
+#include "demangle.h"
+#include "objfiles.h"
+#include "gdb_assert.h"
-#include "opcode/arc.h"
-
-#include "arc-tdep.h"
-
+/* ARC header files */
-//#define ARC_DEBUG 1
+/* N.B. one and only one of ARC_ELF32_TARGET and ARC_LINUX_TARGET must be defined! */
-#if ARC_DEBUG
-# define ENTERMSG printf ("--- entered %s:%s()\n", __FILE__, __FUNCTION__)
-# define ENTERARGS(fmt, args...) printf ("--- entered %s:%s(" fmt ")\n", __FILE__, __FUNCTION__, args)
-# define LEAVEMSG printf ("--- exited %s:%s()\n", __FILE__, __FUNCTION__)
+#ifdef ARC_ELF32_TARGET
+#ifdef ARC_LINUX_TARGET
+#error ARC build is not correctly configured (both flags set)
#else
-# define ENTERMSG
-# define ENTERARGS(fmt, args...)
-# define LEAVEMSG
+#include "config/arc/tm-embed.h"
+#endif
+#else
+#ifdef ARC_LINUX_TARGET
+#include "config/arc/tm-linux.h"
+#else
+#error ARC build is not correctly configured (no flag set)
+#endif
#endif
-#define ARC_STATUS32_A1 0x8
-#define ARC_STATUS32_A2 0x10
-#define ARC_STATUS32_AE 0x20
-#define ARC_STATUS32_L 0x100
+#include "opcode/arc.h"
+#include "opcodes/arc-dis.h"
+#include "opcodes/arc-ext.h"
+#include "opcodes/arcompact-dis.h"
+#include "arc-support.h"
+#include "arc-tdep.h"
-static CORE_ADDR arc_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp);
-/* The frame unwind cache for the ARC
- */
+/* -------------------------------------------------------------------------- */
+/* local types */
+/* -------------------------------------------------------------------------- */
-struct arc_unwind_cache
+typedef struct
{
- /* blink offset in stack */
- int blink_offset ;
+ const char *name;
+ CORE_ADDR address;
+ Boolean is_argument;
+ Boolean is_callee;
+ Boolean is_array;
+ unsigned int size;
+ unsigned int element_size;
+} LocalVariable;
- /* Caller's PC */
- CORE_ADDR prev_pc;
-
- /* The stack pointer at the time this frame was created; i.e. the
- caller's stack pointer when this function was called. It is used
- to identify this frame. */
-
- CORE_ADDR prev_sp;
- /* The frame base */
- CORE_ADDR frame_base;
- /* Frame size */
- int framesize;
-
- /* offset of sp from the stack frame base */
- LONGEST sp_offset;
- /* offset of fp from the stack frame base */
- LONGEST fp_offset;
- /* Is this a leaf function */
- int is_leaf ;
- /* Is there a frame pointer */
- int uses_fp;
-
-
- /* Offsets for each register in the stack frame */
- struct trad_frame_saved_reg *saved_regs;
-};
+/* The frame unwind cache for the ARC. */
+typedef struct
+{
+ /* BLINK save location offset from previous SP (-ve value). */
+ int blink_save_offset_from_prev_sp;
+ /* The stack pointer at the time this frame was created; i.e. the
+ caller's stack pointer when this function was called. It is used
+ to identify this frame. */
+ CORE_ADDR prev_sp;
+ /* The frame base (as held in FP).
+ N.B. this is NOT the address of the lowest word in the frame! */
+ CORE_ADDR frame_base;
-/* Function Prototypes */
+ /* Change in SP from previous SP (-ve value) - this is computed by scanning
+ the prologue of the function: initially 0, it is updated for each
+ instruction which changes SP (either explicitly by a subtraction from SP
+ or implicitly by a push operation), so at each point in the prologue it
+ gives the difference between the previous SP (i.e. before the function
+ was called) and the current SP at that point; at the end of the prologue
+ it holds the total change in SP, i.e. the size of the frame. */
+ LONGEST delta_sp;
-static CORE_ADDR arc_unwind_sp (struct gdbarch *gdbarch,
- struct frame_info *next_frame);
+ /* Offset of old stack pointer from frame base (+ve value). */
+ LONGEST old_sp_offset_from_fp;
-static CORE_ADDR arc_unwind_pc (struct gdbarch *gdbarch,
- struct frame_info *next_frame);
+ /* Is this a leaf function? */
+ Boolean is_leaf;
+ /* Is there a frame pointer? */
+ Boolean uses_fp;
-static struct arc_unwind_cache * arc_frame_unwind_cache (struct frame_info *next_frame,
- void ** this_prologue);
+ /* Offsets for each register in the stack frame. */
+ struct trad_frame_saved_reg *saved_regs;
+ unsigned int saved_regs_mask;
+} UnwindCache;
-static CORE_ADDR arc_scan_prologue (CORE_ADDR pc,
- struct frame_info *next_frame,
- struct arc_unwind_cache *info);
+/* -------------------------------------------------------------------------- */
+/* local data */
+/* -------------------------------------------------------------------------- */
-static int arc_binutils_reg_to_regnum (int reg);
+#define DEBUG_COMMAND "arc-debug"
+#define SHOW_FRAME_COMMAND "arc-show-frame"
+#define SHOW_FRAME_COMMAND_USAGE "Usage: " SHOW_FRAME_COMMAND " [ <FRAME> ]\n"
-extern struct arcDisState arcAnalyzeInstr ( bfd_vma address,disassemble_info* info );
-extern struct arcDisState a4AnalyzeInstr ( bfd_vma address,disassemble_info* info );
+#define NEW_LINE _("\n")
-/* defined in opcodes, but there's no header file with this prototype... */
-disassembler_ftype arcompact_get_disassembler (void *);
-/* Standard register type for the ARC platform .
- * It would be builtin_type_uint32 until
- * we consider the DSP extensions
- */
+/* -------------------------------------------------------------------------- */
+/* externally visible data */
+/* -------------------------------------------------------------------------- */
-static struct type *
-arc_register_type (struct gdbarch *gdbarch, int regnum)
-{
- return builtin_type_uint32;
-}
+/* Global debug flag. */
+Boolean arc_debug_target;
-void printFrameInfo(struct arc_unwind_cache * info)
-{
-#ifdef ARC_DEBUG
- printf("-------------------\n");
- printf("%lx \n",info );
- printf("prev_sp = %lx \n",info->prev_sp);
- printf("prev_pc = %lx \n",info->prev_pc);
- printf("frame_base is %lx \n",info->frame_base);
- printf("framesize is %lx \n",info->framesize);
- printf("Blink offset %lx \n",info->blink_offset);
- printf("sp_offset = %lx \n",info->sp_offset );
- printf("fp_offset is %lx \n",info->fp_offset);
- printf("is_leaf = %d, uses_fp=%d",info->is_leaf, info->uses_fp);
-#endif
-}
+/* -------------------------------------------------------------------------- */
+/* local macros */
+/* -------------------------------------------------------------------------- */
-/* Print the instruction state returned
- by the disassembler . Used for internal
- debugging only
-*/
+#define WORD_ALIGNED(addr) ((addr) & ~(BYTES_IN_WORD - 1))
+#define WORDS_OCCUPIED(bytes) (((bytes) + BYTES_IN_WORD - 1) / BYTES_IN_WORD)
+#define ROUND_UP_TO_WORDS(bytes) (WORDS_OCCUPIED(bytes) * BYTES_IN_WORD)
-void printInsnState(struct arcDisState state)
-{
-#ifdef ARC_DEBUG
- printf("---------------------------------\n");
- printf("Instruction Length %d\n", state.instructionLen);
- printf("Opcode [0x%x] : Cond [%x]\n", state._opcode, state._cond);
- printf("Words 1 [%lx] : 2 [%lx]\n", state.words[0], state.words[1]);
- printf("ea present [%x] : memload [%x]\n", state._ea_present, state._mem_load);
- printf("Load length [%d]:\n", state._load_len);
- printf("Address writeback [%d]\n", state._addrWriteBack);
- printf("ea reg1 is [%x] offset [%x] \n", state.ea_reg1, state._offset);
- printf("ea reg2 is [%x] \n", state.ea_reg2);
- printf("operands buffer is %s \n", state.operandBuffer);
- printf("SourceType is %d \n",state.sourceType);
- printf("Flow is %d\n",state.flow);
- printf("Branch is %d,'%c'\n",state.isBranch, state.isBranch);
-#endif
-}
-/* Scan the prologue and update the
- * corresponding frame cache for the frame unwinder for unwinding
- * frames without debug info . In such a situation GDB attempts to
- * parse the prologue for this purpose . This currently would attempt
- * to parse the prologue generated by our gcc 2.95 .(We should support
- * Metaware generated binaries at some suitable point of time )
- * This function is called with the pc where gdb stopped , the next_frame
- * to be filled in (if need be?) and the existing cached info .
-
- * scan_prologue is called by our unwinder as well
- * as from skip_prologue in the case that it cannot detect
- * the end of the prologue. next_frame is set to NULL if we are called from
- * arc_skip_prologue in an attempt to discover the end of the prologue. In
- * such a case we don't fill the frame info that is passed to us :-)
-
- * Todos.
- * 1. Support 32 bit normal frames generated by GCC 2.95 .
- * 2. Support 16 and 32 bit mixed frames generated by GCC 2.95
- * 3. Support 32 bit normal variadic function frames by GCC 2.95
- * 4. Support 32 bit normal frames from GCC 3.4.x with variadic args
- * 5. Support 16 and 32 bit normal frames from GCC 3.4.x with variadic args
- * 6. Support 16 and 32 bit mixed frames generated by GCC 3.4.x
- * 7. Support Metaware generated prologues .( The difference is
- * in the use of thunks to identify the saving and restoring of
- * callee saves :-) May have to do some hackery even in next_pc.
- * since the call is going to create its own set of problems
- * with our stack setup :-(
- * We attempt to use the disassembler interface from the opcodes
- * library to do our disassembling .
-
- * The usual 32 bit normal
- * gcc -O0 prologue looks like this.
-
- * Complete Prologue for all GCC frames (Cases #1 to #6 in Todos above)
-
- * sub sp, sp, limm ; space for variadic arguments.
- * st.a blink, [sp,-4] ; push blink (if not a leaf function)
- * sub sp, sp , limm ; (optional space creation for callee saves )
- * st r13, [sp] ; pushes of all callee saves.
- * st r14, [sp,4] ; pushes of more callee saves.
- * XXXX
- * st.a fp , [sp,-4] ; push fp (if fp has to be saved )
- * mov fp , sp ; Set the current frame up correctly
- * sub sp , sp , #immediate ; Create space for local vars on the stack.
- */
+/* Macros to be used with disassembling the prologue and update the frame info.
+ The *FI macros are to update the frame info and the ACT macros are to
+ actually do the action on a corresponding match. */
+#define IS_INSTRUCTION(insn_name, search_string) !strcmp(insn_name, search_string)
+#define CHECK_OPERAND_STRING_AND_ACT(target_check, search_string, action) \
+ if (strstr(target_check, search_string) == target_check) \
+ { \
+ action; \
+ return TRUE; \
+ }
-/* Macros to be used with disassembling the prologue
- * and update the frame info.The *FI macros are to update
- * the frame info and the ACT macros are to actually do the
- * action on a corresponding match.
- *
-*/
-#define CHECKOPDSTRING(targetcheck,searchstring) \
- if(strstr(targetcheck,searchstring) == targetcheck) \
- {continue;}
-
-#define CHECKOPDSTRINGANDACT(targetcheck,searchstring,action) \
- if(strstr(targetcheck,searchstring) == targetcheck) \
- {\
- action;\
- continue;}
-
-
-/* The frame info changes by changing the decrementing
- the sp_offset and setting the leaf function to be NIL;
- Also the offset of the blink register from the previous
- value of sp is calculated. Finally this can be updated
- as
- info->blink_offset = info-> prev_sp + info->blink_offset ;
- Also the addition below is coz the offsets are usually negative
-*/
-#define PUSHBLINKACT do { \
- if(info) \
- { \
- info->sp_offset += current_instr._offset; \
- info->blink_offset = info->sp_offset ; \
- info->is_leaf = 0;\
- }}while(0);
-
+/* The frame info changes by changing the decrementing the delta_sp and setting
+ the leaf function flag to be False (if this function prologue is saving blink
+ then it must be going to call another function - so it can not be a leaf!);
+ also the offset of the blink register save location from the previous value
+ of sp is recorded. This will eventually used to compute the address of the
+ save location:
-#define ISPUSHBLINK(state) CHECKOPDSTRING(state.operandBuffer,"blink")
-#define ISPUSHBLINKFI(state) CHECKOPDSTRINGANDACT(state.operandBuffer,"blink",PUSHBLINKACT)
+ <blink saved address> = <prev sp> + <blink offset from prev sp>
+ The addition (+=) below is because the sp offset and the instruction offset
+ are negative - so incrementing the sp offset by the instruction offset is
+ actually making the sp offset more negative, correctly reflecting that SP
+ is moving further down the downwards-growing stack. */
-#define PUSHFPACT do { \
- if(info) \
- { \
- info->sp_offset += current_instr._offset ; \
- info->fp_offset = -info->sp_offset; \
- }}while(0);
-
-#define ISPUSHFP(state) CHECKOPDSTRING(state.operandBuffer,"fp")
-#define ISPUSHFPFI(state) CHECKOPDSTRINGANDACT(state.operandBuffer,"fp",PUSHFPACT)
-#define ISINSTRUCTION(insnname,searchstring) !strcmp(insnname,searchstring)
+#define PUSH_BLINK(offset) \
+ { \
+ info->delta_sp += offset; \
+ info->blink_save_offset_from_prev_sp = (int) info->delta_sp; \
+ info->is_leaf = FALSE; \
+ }
+#define PUSH_BLINK_ACT \
+ do { \
+ if (info) PUSH_BLINK(instr->_offset) \
+ } while (0);
-#define UPDATEFPACT do {\
- if(info) {\
- info->uses_fp = 1;\
- }}while(0);
+#define IS_PUSH_BLINK_FI(state) CHECK_OPERAND_STRING_AND_ACT(state->operandBuffer, "blink", PUSH_BLINK_ACT)
+/* At the point that that FP is pushed onto the stack (so saving the dynamic
+ link chain pointer to the previous frame), at the address that will be the
+ base of the new frame, we know the offset of SP from the previous SP - so the
+ offset of the old SP from the new frame base is known (the -ve delta_sp is
+ negated to give the +ve old_sp_offset_from_fp). */
+#define PUSH_FP_ACT do { \
+ if (info) \
+ { \
+ info->delta_sp += instr->_offset; \
+ info->old_sp_offset_from_fp = -info->delta_sp; \
+ }} while (0);
-#define ISUPDATEFPFI(state) \
- if(ISINSTRUCTION(state.instrBuffer,"mov")) \
-{ \
- CHECKOPDSTRINGANDACT(state.operandBuffer,"fp,sp",UPDATEFPACT); \
-}
+#define IS_PUSH_FP_FI(state) CHECK_OPERAND_STRING_AND_ACT(state->operandBuffer, "fp", PUSH_FP_ACT)
+#define UPDATE_FP_ACT do { \
+ if (info) \
+ info->uses_fp = TRUE; \
+ } while (0);
-#define ISUPDATEFP(state) \
- if(ISINSTRUCTION(state.instrBuffer,"mov")) \
-{ \
- CHECKOPDSTRING(state.operandBuffer,"fp,sp") \
-}
+#define IS_UPDATE_FP_FI(state) \
+ if (IS_INSTRUCTION(state->instrBuffer, "mov")) \
+ { \
+ CHECK_OPERAND_STRING_AND_ACT(state->operandBuffer, "fp,sp", UPDATE_FP_ACT); \
+ }
+#define UPDATE_STACK_SPACE(state) do { \
+ if (info) { \
+ /* Eat up sp,sp. */ \
+ int immediate = atoi(state->operandBuffer + 6); \
+ info->delta_sp -= immediate; \
+ }} while (0);
-#define ISSUBSP(state) \
-if(ISINSTRUCTION(state.instrBuffer,"sub"))\
-{ \
- CHECKOPDSTRING(state.operandBuffer,"sp,sp") \
+
+#define IS_SUB_SP_FI(state) \
+ if (IS_INSTRUCTION(state->instrBuffer, "sub") || \
+ IS_INSTRUCTION(state->instrBuffer, "sub_s")) \
+ { \
+ CHECK_OPERAND_STRING_AND_ACT(state->operandBuffer, "sp,sp", UPDATE_STACK_SPACE(state)) \
+ }
+
+
+/* -------------------------------------------------------------------------- */
+/* forward declarations */
+/* -------------------------------------------------------------------------- */
+
+static CORE_ADDR scan_prologue (CORE_ADDR entrypoint,
+ struct frame_info *next_frame,
+ UnwindCache *info);
+
+
+/* -------------------------------------------------------------------------- */
+/* local debug functions */
+/* -------------------------------------------------------------------------- */
+
+/* Print information for a frame. */
+
+static void
+printFrameInfo (const char *message,
+ UnwindCache *info,
+ Boolean addresses_known)
+{
+ unsigned int i;
+
+ DEBUG("-------------------\n");
+ DEBUG("%s (info = %p)\n", message, info);
+ DEBUG("prev_sp = %lx\n", (long unsigned int) info->prev_sp);
+ DEBUG("frame_base = %lx\n", (long unsigned int) info->frame_base);
+ DEBUG("blink offset = %d\n", info->blink_save_offset_from_prev_sp);
+ DEBUG("delta_sp = %d\n", (int) info->delta_sp);
+ DEBUG("old_sp_offset_from_fp = %d\n", (int) info->old_sp_offset_from_fp);
+ DEBUG("is_leaf = %d, uses_fp = %d\n", info->is_leaf, info->uses_fp);
+
+ for (i = ARC_ABI_FIRST_CALLEE_SAVED_REGISTER; i < ARC_ABI_LAST_CALLEE_SAVED_REGISTER; i++)
+ {
+ if (info->saved_regs_mask & (1 << i))
+ DEBUG("saved register R%02d %s 0x%lx\n",
+ i,
+ (addresses_known) ? "address" : "offset",
+ (unsigned long) info->saved_regs[i].addr);
+ }
+ DEBUG("-------------------\n");
}
-#define UPDATESTACKSPACE(state) do { \
- if(info){ \
-/* Eat up sp,sp */ \
- int tmp = atoi(state.operandBuffer + 6); \
- info->sp_offset -= tmp; \
- }}while(0);
+
+static const char*
+ARC_Debugger_OperandType_Image (enum ARC_Debugger_OperandType value)
+{
+ switch (value)
+ {
+ case ARC_LIMM : return "LIMM";
+ case ARC_SHIMM : return "SHIMM";
+ case ARC_REGISTER : return "REGISTER";
+ case ARCOMPACT_REGISTER: return "COMPACT REGISTER";
+ case ARC_UNDEFINED : return "UNDEFINED";
+ }
+ return "?";
+}
-#define ISSUBSPFI(state) \
-if(ISINSTRUCTION(state.instrBuffer,"sub") \
- || ISINSTRUCTION(state.instrBuffer,"sub_s"))\
-{ \
- CHECKOPDSTRINGANDACT(state.operandBuffer,"sp,sp",UPDATESTACKSPACE(state)) \
+/* Print the instruction state returned by the disassembler.
+ Used for internal debugging only. */
+
+static void
+printInsnState (struct arcDisState state)
+{
+ DEBUG("---------------------------------\n");
+ DEBUG("Instruction Length %d\n", state.instructionLen);
+ DEBUG("Opcode [0x%x] : Cond [%x]\n", state._opcode, state._cond);
+ DEBUG("Words 1 [%lx] : 2 [%lx]\n", state.words[0], state.words[1]);
+ DEBUG("Ea present [%x] : memload [%x]\n", state._ea_present, state._mem_load);
+ DEBUG("Load Length [%d]:\n", state._load_len);
+ DEBUG("Address Writeback [%d]\n", state._addrWriteBack);
+ DEBUG("EA reg1 is [%x] offset [%x]\n", state.ea_reg1, state._offset);
+ DEBUG("EA reg2 is [%x]\n", state.ea_reg2);
+ DEBUG("Instr buffer is %s\n", state.instrBuffer);
+ DEBUG("Operand buffer is %s\n", state.operandBuffer);
+ DEBUG("SourceType is %s\n", ARC_Debugger_OperandType_Image(state.sourceType));
+ DEBUG("Source operand is %u\n", state.source_operand.registerNum); /* All fields of the union have same type. */
+ DEBUG("Flow is %d\n", state.flow);
+ DEBUG("Branch is %d\n", state.isBranch);
+ DEBUG("---------------------------------\n");
}
-/*Function to scan the prologue of a A4 binary
-
-ARCtangent-A4 Prolog
- The stack back-trace data structure is a 16-byte structure which is
- used to save the return register (blink, 4 bytes), the frame pointer
- register (fp, 4-bytes) and 8-bytes is reserved.
-
- The compiler-generated prolog code does the following:
- --> Allocates space for register arguments in case of variadic function
- (functions with variable argument lists)
- --> Saves the return address register (blink)
- --> Saves the caller's frame pointer (fp), if required, and
- sets the new frame pointer to this location
- --> Decrements the stack pointer to account for the new stack frame
- --> Saves required non-volatile general-purpose registers into the
- register save area
- --> Stores the arguments above the stack back-trace data structure
-
-
- Demo Patterns:
- st blink,[sp,4] ; Saves the return address
- st fp,[sp] ; Saves the callers frame pointer
- mov fp,sp ; Saves
- sub sp,sp,24
-
-0xa 538e7e20 sub sp,sp,32 ; Space for variadic args
-0x2 100e3e04 st blink,[sp,4] ; Saves the return address
-0x2 100e3600 st fp,[sp] ; Saves the callers frame pointer
-0xc 636e3800 mov fp,sp ; Resets stack pointer to fp
-0xa 538e7e18 sub sp,sp,24 ; Decrements sp to add for new
- ; stack frame
-0x2 100d81fc st r0,[fp,-4] ; Stores the args
-0x2 100d83f8 st r1,[fp,-8] ; ----"-------
- ...
-*/
+/* -------------------------------------------------------------------------- */
+/* local functions for the disassembler */
+/* -------------------------------------------------------------------------- */
-/* FIXMEA:
-called from arc_skip_prologue as
- skip_pc = arc_scan_prologue(pc,NULL,NULL);
- Then it is supposed to return the first valid pc
- after the prologue
+/* Wrapper for the target_read_memory function. */
- Prologue analysis does the rest...
- Currently our scan prologue does not
- support getting input for the frame unwinder
+static int
+read_memory_for_disassembler (bfd_vma memaddr,
+ bfd_byte *myaddr,
+ unsigned int length,
+ struct disassemble_info *info) // unused
+{
+ return target_read_memory((CORE_ADDR) memaddr, (gdb_byte*) myaddr, (int) length);
+}
-
- pc = frame_func_unwind(next_frame);
- arc_scan_prologue (pc, next_frame, info);
-*/
+/* This is a callback function which gets called by gdb whenever the current
+ object file changes. */
-#ifdef ARC4_JTAG
-static CORE_ADDR
-a4_scan_prologue (CORE_ADDR pc, struct frame_info *next_frame,
- struct arc_unwind_cache *info)
+static void
+set_disassembler (struct objfile *objfile)
{
- /* End of prologue */
- CORE_ADDR prologue_ends_pc = pc;
- int i = 0;
- struct arcDisState current_instr, instr_in_delay;
- int insn_length;
-
- /* Initializations to use the opcodes
- * library .
- */
-
- struct disassemble_info di;
-
- unsigned int saved_regs_mask = 0;
- /* An arbitrary length on the length of the
- prologue. If next_frame is NULL this means that there was
- no debug info and we are called from arc_skip_prologue
- */
- /*FIXMEANOW: pc + 64 is probably the max size of the prologue*/
- CORE_ADDR final_pc = (next_frame)?frame_pc_unwind(next_frame):pc+(16*4);
-
-
-
-
- if (info)
+ if (objfile)
{
- /* All functions start as leaf functions until
- we identify push blink
- */
- info->is_leaf = 1;
+ /* The ARC libopcodes wants obfd so that it can find out what CPU
+ extensions are defined in the file. */
+ set_gdbarch_print_insn(current_gdbarch, arcompact_get_disassembler(objfile->obfd));
+// dump_ARC_extmap();
}
-
-
-
- /* Initializations to use the opcodes
- * library .
- */
- init_disassemble_info(&di, gdb_stderr, fprintf_unfiltered);
- di.arch = gdbarch_bfd_arch_info(current_gdbarch)->arch;
- di.mach = gdbarch_bfd_arch_info(current_gdbarch)->mach;
- di.endian = gdbarch_byte_order(current_gdbarch);
- di.read_memory_func = target_read_memory;
-
-
- for (prologue_ends_pc= pc;
- prologue_ends_pc< final_pc;
- prologue_ends_pc += current_instr.instructionLen ) /*FIXMEA: This could as
- well be 4 */
+}
+
+
+/* This function is supplied to gdb as the disassembler until such time as we do
+ have a disassembler available. */
+
+static int
+dummy_disassembler (bfd_vma address, disassemble_info *info)
+{
+ error(_("No disassembly operation yet available (no executable file loaded)"));
+ return 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* local functions for decoding call chains */
+/* -------------------------------------------------------------------------- */
+
+/* Simple utility function to create a new frame cache structure. */
+
+static UnwindCache*
+create_cache (struct frame_info *next_frame)
+{
+ UnwindCache *cache = FRAME_OBSTACK_ZALLOC (UnwindCache);
+
+ /* Zero all fields. */
+ cache->blink_save_offset_from_prev_sp = 0;
+ cache->prev_sp = 0;
+ cache->frame_base = 0;
+ cache->delta_sp = 0;
+ cache->old_sp_offset_from_fp = 0;
+ cache->is_leaf = FALSE;
+ cache->uses_fp = FALSE;
+
+ /* Allocate space for saved register info. */
+ cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
+
+ return cache;
+}
+
+
+/* Compute the previous frame's stack pointer (which is also the frame's ID's
+ stack address), and this frame's base pointer. */
+
+static void
+find_previous_stack_pointer (UnwindCache *info,
+ struct frame_info *next_frame)
+{
+ ENTERARGS("next_frame = %p", next_frame);
+
+ /* If the frame has a frame pointer. */
+ if (info->uses_fp)
{
- current_instr = a4AnalyzeInstr(prologue_ends_pc, &di);
- printInsnState(current_instr);
-#ifdef ARC_DEBUG
- printf("Prologue PC: %d\n", prologue_ends_pc);
- printf("Final PC: %d\n", final_pc);
-#endif
+ ULONGEST this_base;
+ unsigned int i;
+
+ /* The SP was moved to the FP. This indicates that a new frame
+ was created. Get THIS frame's FP value by unwinding it from
+ the next frame. The old contents of FP were saved in the location
+ at the base of this frame, so this also gives us the address of
+ the FP save location. */
+ this_base = frame_unwind_register_unsigned(next_frame, ARC_FP_REGNUM);
+ info->frame_base = (CORE_ADDR) this_base;
+ info->saved_regs[ARC_FP_REGNUM].addr = (long long) this_base;
+
+ /* The previous SP is the current frame base + the difference between
+ that frame base and the previous SP. */
+ info->prev_sp = info->frame_base + (CORE_ADDR) info->old_sp_offset_from_fp;
+
+ for (i = ARC_ABI_FIRST_CALLEE_SAVED_REGISTER; i < ARC_ABI_LAST_CALLEE_SAVED_REGISTER; i++)
+ {
+ /* If this register has been saved, add the previous stack pointer
+ to the offset from the previous stack pointer at which the
+ register was saved, so giving the address at which it was saved. */
+ if (info->saved_regs_mask & (1 << i))
+ {
+ info->saved_regs[i].addr += info->prev_sp;
+
+#ifdef DUMP_SAVED_REGISTERS
+ /* This is a really useful debugging aid: we can debug a test
+ program which loads known values into the callee-saved
+ registers, then calls another function which uses those
+ registers (and hence must save them) then hits a breakpoint;
+ traversing the stack chain (e.g. with the 'where' command)
+ should then execute this code, and we should see those known
+ values being dumped, so showing that we have got the right
+ addresses for the save locations! */
+ {
+ unsigned int contents;
+ DEBUG("saved R%02d is at 0x%lx\n", i, (long unsigned int) info->saved_regs[i].addr);
- if (current_instr._opcode == 0x2)
- {
- // Saves the return address st blink,[sp,4] 0x100e3e04
- // Save the callers fp st fp,[sp] 0x100e3600
- // Saves the args st rX,[fp, #imm] 0x100d8xxx
- if (current_instr.ea_reg1 == 28)
- {
- if( strstr(current_instr.operandBuffer,"blink") == current_instr.operandBuffer)
- {
- if(info)
- {
- info->sp_offset += current_instr._offset;
- // info->blink_offset = info->sp_offset ;
- info->blink_offset = current_instr._offset;
- info->is_leaf = 0;
-
-#ifdef ARC_DEBUG
- printf("Blink instruction:\n");
- printFrameInfo(info);
-#endif
- }
- continue;
- }
- else
- if(strstr(current_instr.operandBuffer,"fp") == current_instr.operandBuffer)
- {
- if(info)
- {
-/* info->sp_offset += current_instr._offset ; */
-/* info->fp_offset = info->sp_offset; */
- info->fp_offset = 0;
- }
- continue;
- }
- }
- else if (current_instr.ea_reg1 == 27)
- {
- /* Saving of arguments onto the stack using the
- frame pointer (r27). */
- if(info)
- {
- // Save regs offsets
- }
-#ifdef ARC_DEBUG
- printf(" Saving registers onto stack\n%s\n",current_instr.operandBuffer);
-#endif
- continue;
- }
- // ISPUSHBLINK(current_instr);
- }
- else if (current_instr._opcode == 0xc)
- {
- // Resets stack pointer to fp
- // 0x636e3800
- // 636e3800 mov fp,sp
- if (current_instr.words[0] == 0x636e3800)
- {
- if (info)
- {
- info->uses_fp = 1;
- }
- continue;
- }
- }
- else if (current_instr._opcode == 0xa)
- {
- // Decrements stackpointer to add for new stack frame
- // 0x538e7e18 sub sp,sp,#imm
- // 538e7e20 sub sp,sp,32
- if( current_instr.words[0] == 0x538e7e20)
- {
- //sub sp,sp, 32 //// variadic
- if (info)
- {
- int tmp = atoi(current_instr.operandBuffer + 6);
- info->sp_offset -= tmp;
- }
- continue;
- }
- else if((current_instr.words[0] & 0xffffff00) == 0x538e7e00)
- {
- // sub sp,sp,xx
- if(info)
- {
- int tmp = atoi(current_instr.operandBuffer + 6);
- info->sp_offset -= tmp;
- }
- continue;
- }
- }
-
- /* Found a instruction that is not in
- the prologue*/
-#ifdef ARC_DEBUG
- printf("End of Prologue reached \n");
+ if (target_read_memory((CORE_ADDR) info->saved_regs[i].addr,
+ (gdb_byte*) &contents,
+ BYTES_IN_REGISTER) == 0)
+ {
+ DEBUG("saved R%02d contents: 0x%0x\n", i, contents);
+ }
+ }
#endif
- break;
+ }
+ }
}
-
- /* Means we were called from skip_prologue */
- if((next_frame == NULL)&& (info == NULL))
+ else
{
- return prologue_ends_pc;
- }
+ ULONGEST this_sp;
+ /* Get the stack pointer for this frame by getting the saved SP
+ from the next frame. */
+ this_sp = frame_unwind_register_unsigned (next_frame, ARC_SP_REGNUM);
- info->framesize = -info->sp_offset;
- /* Compute the previous frame's stack pointer (which is also the
- frame's ID's stack address), and this frame's base pointer. */
- if(info->uses_fp)
- {
+ /* The previous SP is this frame's SP plus the known difference between
+ the previous SP and this frame's SP (the delta_sp is negated as it is
+ a negative quantity). */
+ info->prev_sp = (CORE_ADDR) (this_sp + (ULONGEST) (-info->delta_sp));
- ULONGEST this_base;
- /* The SP was moved to the FP. This indicates that a new frame
- was created. Get THIS frame's FP value by unwinding it from
- the next frame. */
- frame_unwind_unsigned_register(next_frame, ARC_FP_REGNUM,
- &this_base);
- info->frame_base = this_base;
- info->saved_regs[ARC_FP_REGNUM].addr = info->frame_base;
-
- /* The previous sp is the current frame base + the offset of the
- fp in the current frame */
- info->prev_sp = info->frame_base + info->fp_offset;
- for(i = 13; i < 26 ; i++ )
- {
- if(saved_regs_mask & (1 << i))
- info->saved_regs[i].addr += info->frame_base ;
- }
-
- printFrameInfo(info);
-
+ /* Assume that the FP is this frame's SP. */
+ info->frame_base = (CORE_ADDR) this_sp;
}
- else
+
+ /* If the function owning this frame is not a leaf function. */
+ if (!info->is_leaf)
{
- ULONGEST this_base;
- /* Assume that the FP is this frame's SP but with that pushed
- stack space added back. */
- frame_unwind_unsigned_register (next_frame, ARC_SP_REGNUM, &this_base);
- info->frame_base = this_base;
-
- /* In such a case it would be the previous SP + the size of the current frame */
- info->prev_sp = info->frame_base + info->framesize;
-
+ /* Usually blink is saved above the callee save registers and below the
+ space created for variable arguments. The quantity
+
+ info->blink_save_offset_from_prev_sp
+
+ is negative, so adding it to the the previous SP gives the address of
+ a location further down the stack from that SP. */
+ info->saved_regs[ARC_BLINK_REGNUM].addr =
+ (LONGEST) (info->prev_sp + info->blink_save_offset_from_prev_sp);
}
-
+}
+
- if(!info->is_leaf)
+/* The workhorse : frame_unwind_cache for the ARC700 target. */
+
+static UnwindCache *
+frame_unwind_cache (struct frame_info *next_frame,
+ void **this_prologue_cache)
+{
+ ENTERMSG;
+
+ if ((*this_prologue_cache) == NULL)
{
+ CORE_ADDR entrypoint = frame_func_unwind(next_frame, NORMAL_FRAME);
+ UnwindCache *cache = create_cache(next_frame);
- /* Usually blink is saved before the callee save registers and
- below the space created for variadic arguments . We maintain
- info->blink_offset as negative when we stored it initially
- */
- info->saved_regs[ARC_BLINK_REGNUM].addr = info->prev_sp + info->blink_offset;
-#ifdef ARC_DEBUG
- printf("blink offset is [%x] \n",info->blink_offset);
-#endif
+ /* Return the newly-created cache. */
+ *this_prologue_cache = cache;
+
+ /* Prologue analysis does the rest... */
+
+ /* Currently our scan prologue does not support getting input for the
+ frame unwinder. */
+ (void) scan_prologue(entrypoint, next_frame, cache);
}
-
- /* The PC is found in blink (the actual register or located on the stack). */
- // FIXMEA:
- //info->saved_regs[ARC_STATUS_REGNUM] |= (info->saved_regs[ARC_BLINK_REGNUM] & 0xffffff)>>2;
- info->saved_regs[ARC_STATUS_REGNUM] = info->saved_regs[ARC_BLINK_REGNUM];
- return prologue_ends_pc;
+ return *this_prologue_cache;
}
-#endif
-static CORE_ADDR
-arc_scan_prologue (CORE_ADDR pc, struct frame_info *next_frame,
- struct arc_unwind_cache *info)
+
+/* -------------------------------------------------------------------------- */
+/* local functions for decoding function prologues */
+/* -------------------------------------------------------------------------- */
+
+/* This function determines whether the given register, which is being saved
+ by a function prologue on the stack at a known offset from the current SP,
+ is a callee-saved register. If it is, the information in the frame unwind
+ cache is updated. */
+
+static Boolean
+is_callee_saved_register (unsigned int reg,
+ int offset,
+ UnwindCache * info)
{
-#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf("\narc_scan_prologue called\n");
-#endif
-#else
- /* End of prologue */
- CORE_ADDR prologue_ends_pc = pc;
- int i = 0;
- struct arcDisState current_instr, instr_in_delay;
- int insn_length;
-
- /* Initializations to use the opcodes
- * library .
- */
-
- struct disassemble_info di;
-
- unsigned int saved_regs_mask = 0;
- /* An arbitrary length on the length of the
- prologue. If next_frame is NULL this means that there was
- no debug info and we are called from arc_skip_prologue
- */
- CORE_ADDR final_pc = (next_frame)?frame_pc_unwind(next_frame):pc+64;
-
-
-
-
- if (info)
- {
- /* All functions start as leaf functions until
- we identify push blink
- */
- info->is_leaf = 1;
-
- }
-
-
-
- /* Initializations to use the opcodes
- * library .
- */
- init_disassemble_info(&di, gdb_stderr, fprintf_unfiltered);
- di.arch = gdbarch_bfd_arch_info(current_gdbarch)->arch;
- di.mach = gdbarch_bfd_arch_info(current_gdbarch)->mach;
- di.endian = gdbarch_byte_order(current_gdbarch);
- di.read_memory_func = target_read_memory;
-
-
- for(prologue_ends_pc= pc; prologue_ends_pc< final_pc;
- prologue_ends_pc += current_instr.instructionLen )
+ if (ARC_ABI_FIRST_CALLEE_SAVED_REGISTER <= reg && reg <= ARC_ABI_LAST_CALLEE_SAVED_REGISTER)
{
- current_instr = arcAnalyzeInstr(prologue_ends_pc, &di);
- printInsnState(current_instr);
- /* Might be a push or a pop */
- if(current_instr._opcode == 0x3)
- {
- if(current_instr._addrWriteBack)
- {
- /* This is a st.a */
- if((current_instr.ea_reg1 == 28) &&
- (current_instr._offset == -4))
- {
-
- /* This is a push something at sp */
- /* Is it a push of the blink */
- ISPUSHBLINKFI(current_instr);
- /* Is it a push for fp */
- ISPUSHFPFI(current_instr);
-
- }
-
- }
- else
- {
- /* Is this a store of some register onto
- the stack using the stack pointer.*/
- if(current_instr.ea_reg1 == 28)
- {
- if(current_instr.sourceType == ARC_REGISTER )
- {
- /* R13..R26 are the callee saved registers. [R27 (fp)
- is also a callee saved register, but it's usually
- pushed using st.a and so handled in the st.a case
- above.] */
- if((current_instr.source_operand.registerNum > 12
- && current_instr.source_operand.registerNum <= 26))
- {
- if(info)
- {
- printFrameInfo(info);
- /* Save up the offsets for the correct instruction */
- info->saved_regs[current_instr.source_operand.registerNum].addr
- = - info->sp_offset - current_instr._offset;
- saved_regs_mask |= (1 << current_instr.source_operand.registerNum);
- }
- continue;
- }
-
- }
-
- }
- /* Is this the store of some register on the
- stack using the frame pointer. We check
- for argument registers getting saved and
- restored.
- */
- if(current_instr.ea_reg1 == 27)
- if((current_instr.source_operand.registerNum <= 7))
- {
- /* Saving argument registers.Don't save them in saved_regs, just skip.
- */
- continue;
- }
-
-
-
- }
- }
-
- if(current_instr._opcode == 0x4)
- {
- /* A major opcode 0x4 instruction */
- /* We are usually interested in a
- mov or a sub */
- ISUPDATEFPFI(current_instr);
- ISSUBSPFI(current_instr);
- }
- if(current_instr._opcode == 0x18)
- {
- /* sub_s sp,sp,constant */
- ISSUBSPFI(current_instr);
- /* push_s blink */
- if(strcmp(current_instr.instrBuffer,"push_s") == 0)
- {
- if(strcmp(current_instr.operandBuffer,"blink") == 0)
- {
- if(info)
- {
- info->sp_offset += 4;
- info->blink_offset = info->sp_offset ;
- info->is_leaf = 0;
- }
- continue;
- }
- }
- }
-
- /* If we reach here . we have
- * reached end of the prologue
- */
- break;
-
- }
-
- /* Means we were called from skip_prologue */
- if((next_frame == NULL)&& (info == NULL))
- {
- return prologue_ends_pc;
+ DEBUG("register R%02u saved\n", reg);
+
+ if (info)
+ {
+ /* We can not determine the address of the location in the stack
+ frame in which the register was saved, as we do not (yet) know
+ the frame or stack pointers for the frame; so the most we can do
+ is to record the offset from the old SP of that location, which
+ we can compute as we know the offset of SP from the old SP, and
+ the offset of the location from SP (which is the offset in the
+ store instruction).
+
+ N.B. the stack grows downward, so the store offset is positive,
+ but the delta-SP is negative, so the save offset is also
+ negative.
+
+ | |
+ old sp ------> |------------|
+ / | | \
+ : | | :
+ : | | : -ve
+ : | | : save offset
+ : |------------| :
+ -ve : | save loc | /
+ delta sp : |------------| <--- store address
+ : / | |
+ : +ve : | |
+ : store : | |
+ : offset : | |
+ \ \ | |
+ sp' ---------> | |
+ | |
+ | |
+ |------------| <---- frame base
+ | |
+ | |
+ | |
+ | | |
+ |
+ V
+ downwards
+
+ where sp' is the stack pointer at the current point in the code */
+
+ info->saved_regs[reg].addr = info->delta_sp + offset;
+
+ /* We now know that this register has been saved, so set the
+ corresponding bit in the save mask. */
+ info->saved_regs_mask |= (1 << reg);
+
+ printFrameInfo("after callee register save", info, FALSE);
+
+ return TRUE;
+ }
}
-
-
- info->framesize = -info->sp_offset;
- /* Compute the previous frame's stack pointer (which is also the
- frame's ID's stack address), and this frame's base pointer. */
- if(info->uses_fp)
+
+ return FALSE;
+}
+
+
+/* This function determines whether the given disassembled instruction may be
+ part of a function prologue. If it is, the information in the frame unwind
+ cache may be updated. */
+
+static Boolean
+is_in_prologue (UnwindCache *info, struct arcDisState *instr)
+{
+ /* Might be a push or a pop */
+ if (instr->_opcode == 0x3)
{
+ if (instr->_addrWriteBack != (char) 0)
+ {
+ /* This is a st.a instruction. */
+ if (instr->ea_reg1 == ARC_ABI_STACK_POINTER)
+ {
+ if (instr->_offset == -4)
+ {
+ /* This is a push something at SP. */
+ /* Is it a push of the blink? */
+ IS_PUSH_BLINK_FI(instr);
+
+ /* Is it a push for fp? */
+ IS_PUSH_FP_FI(instr);
+ }
+ else
+ {
+ if (instr->sourceType == ARC_REGISTER )
+ {
+ /* st.a <reg>, [sp,<offset>] */
+
+ if (is_callee_saved_register(instr->source_operand.registerNum,
+ instr->_offset,
+ info))
+ {
+ /* This is a push onto the stack, so change delta_sp. */
+ info->delta_sp += instr->_offset;
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (instr->sourceType == ARC_REGISTER )
+ {
+ /* Is this a store of some register onto the stack using the
+ stack pointer? */
+ if (instr->ea_reg1 == ARC_ABI_STACK_POINTER)
+ {
+ /* st <reg>, [sp,offset] */
+
+ if (is_callee_saved_register(instr->source_operand.registerNum,
+ instr->_offset,
+ info))
+ /* This is NOT a push onto the stack, so do not change delta_sp. */
+ return TRUE;
+ }
- ULONGEST this_base;
- /* The SP was moved to the FP. This indicates that a new frame
- was created. Get THIS frame's FP value by unwinding it from
- the next frame. */
- frame_unwind_unsigned_register(next_frame, ARC_FP_REGNUM,
- &this_base);
- info->frame_base = this_base;
- info->saved_regs[ARC_FP_REGNUM].addr = info->frame_base;
-
- /* The previous sp is the current frame base + the offset of the
- fp in the current frame */
- info->prev_sp = info->frame_base + info->fp_offset;
- for(i = 13; i < 26 ; i++ )
- {
- if(saved_regs_mask & (1 << i))
- info->saved_regs[i].addr += info->frame_base ;
- }
-
- printFrameInfo(info);
-
+ /* Is this the store of some register on the stack using the
+ frame pointer? We check for argument registers getting saved
+ and restored. */
+ if (instr->ea_reg1 == ARC_ABI_FRAME_POINTER)
+ {
+ if (IS_ARGUMENT_REGISTER(instr->source_operand.registerNum))
+ {
+ /* Saving argument registers. Don't set the bits in the
+ saved mask, just skip. */
+ return TRUE;
+ }
+ }
+ }
+ }
}
- else
+
+ else if (instr->_opcode == 0x4)
{
- ULONGEST this_base;
- /* Assume that the FP is this frame's SP but with that pushed
- stack space added back. */
- frame_unwind_unsigned_register (next_frame, ARC_SP_REGNUM, &this_base);
- info->frame_base = this_base;
-
- /* In such a case it would be the previous SP + the size of the current frame */
- info->prev_sp = info->frame_base + info->framesize;
-
+ /* A major opcode 0x4 instruction. */
+ /* We are usually interested in a mov or a sub. */
+ IS_UPDATE_FP_FI(instr);
+ IS_SUB_SP_FI(instr);
}
-
- if(!info->is_leaf)
+ else if (instr->_opcode == 0x18)
{
+ /* sub_s sp,sp,constant */
+ IS_SUB_SP_FI(instr);
- /* Usually blink is saved before the callee save registers and
- below the space created for variadic arguments . We maintain
- info->blink_offset as negative when we stored it initially
- */
- info->saved_regs[ARC_BLINK_REGNUM].addr = info->prev_sp + info->blink_offset;
-#ifdef ARC_DEBUG
- printf("blink offset is [%x] \n",info->blink_offset);
-#endif
+ /* push_s blink */
+ if (strcmp(instr->instrBuffer, "push_s") == 0)
+ {
+ if (strcmp(instr->operandBuffer, "blink") == 0)
+ {
+ if (info)
+ {
+ /* SP is decremented by the push_s instruction (before it
+ stores blink at the stack location addressed by SP). */
+ PUSH_BLINK(-BYTES_IN_REGISTER)
+ }
+ return TRUE;
+ }
+ }
+ else if (strcmp(instr->instrBuffer, "st_s") == 0)
+ {
+ unsigned int reg;
+ int offset;
+
+ if (sscanf(instr->operandBuffer, "r%u,[sp,%d]", &reg, &offset) == 2)
+ {
+ /* st_s <reg>,[sp,<offset>] */
+
+ if (is_callee_saved_register(reg, offset, info))
+ /* This is NOT a push onto the stack, so do not change delta_sp. */
+ return TRUE;
+ }
+ }
}
-
- /* The PC is found in blink (the actual register or located on the stack). */
- info->saved_regs[PC_REGNUM] = info->saved_regs[ARC_BLINK_REGNUM];
- /*info->saved_regs[ARC_PC_REGNUM] = info->saved_regs[ARC_BLINK_REGNUM];*/
- return prologue_ends_pc;
-#endif
+
+ return FALSE;
}
-/* Skip the prologue for the function at pc.
- * This is done by checking from the line
- * information picked up during dwarf reading
- * FIXME: more stuff to be added when we
- * parse the prologue.
- */
+/* Scan the prologue and update the corresponding frame cache for the frame
+ unwinder for unwinding frames without debug info. In such a situation GDB
+ attempts to parse the prologue for this purpose. This currently would attempt
+ to parse the prologue generated by our gcc 2.95 compiler (we should support
+ Metaware generated binaries at some suitable point of time).
+
+ This function is called with:
+ entrypoint : the address of the functon entry point
+ next_frame : the next frame to be filled in (if need be)
+ info : the existing cached info.
+
+ Returns: the address of the first instruction after the prologue.
+
+ This function is called by our unwinder as well as from arc_skip_prologue
+ in the case that it cannot detect the end of the prologue.
+
+ 'next_frame' and 'info' are NULL if this function is called from
+ arc_skip_prologue in an attempt to discover the end of the prologue.
+ In this case we don't fill in the 'info' structure that is passed in.
+
+ TODOs:
+ 1. Support 32 bit normal frames generated by GCC 2.95
+ 2. Support 16 and 32 bit mixed frames generated by GCC 2.95
+ 3. Support 32 bit normal variadic function frames by GCC 2.95
+ 4. Support 32 bit normal frames from GCC 3.4.x with variadic args
+ 5. Support 16 and 32 bit normal frames from GCC 3.4.x with variadic args
+ 6. Support 16 and 32 bit mixed frames generated by GCC 3.4.x
+ 7. Support Metaware generated prologues
+ (The difference is in the use of thunks to identify the saving and
+ restoring of callee saves: may have to do some hackery even in
+ next_pc, since the call is going to create its own set of problems
+ with our stack setup).
+
+ We attempt to use the disassembler interface from the opcodes library to do
+ our disassembling.
+
+ The usual 32 bit normal gcc -O0 prologue looks like this:
+
+ Complete Prologue for all GCC frames (Cases #1 to #6 in TODOs above):
+
+ sub sp, sp, limm ; space for variadic arguments
+ st.a blink, [sp,-4] ; push blink (if not a leaf function) - decrements sp
+ sub sp, sp , limm ; (optional space creation for callee saves)
+ st r13, [sp] ; push of first callee saved register
+ st r14, [sp,4] ; push of next callee saved register
+ ...
+ st.a fp , [sp,-4] ; push fp (if fp has to be saved) - decrements sp
+ mov fp , sp ; set the current frame up correctly
+ sub sp , sp , #immediate ; create space for local vars on the stack */
+
+/* 3 instructions before and after callee saves, and max number of saves; assume each is 4-byte inst. */
+#define MAX_PROLOGUE_LENGTH ((6 + (ARC_ABI_LAST_CALLEE_SAVED_REGISTER - ARC_ABI_FIRST_CALLEE_SAVED_REGISTER + 1)) * 4)
static CORE_ADDR
-arc_skip_prologue (CORE_ADDR pc)
+scan_prologue (CORE_ADDR entrypoint,
+ struct frame_info *next_frame,
+ UnwindCache *info)
{
- //#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_skip_prologue called\n");
-#endif
- // FIXMEA: cleanup #else
- unsigned long inst;
- unsigned long addend = 4;
- CORE_ADDR skip_pc = pc;
- CORE_ADDR func_addr, func_end = 0;
- char *func_name;
- struct symtab_and_line sal;
+ ENTERARGS("next_frame = %p, info = %p", next_frame, info);
- /* If we're in a dummy frame, don't even try to skip the prologue. */
- if (deprecated_pc_in_call_dummy (pc))
- return pc;
+ {
+ /* Will be set to end of prologue. */
+ CORE_ADDR prologue_ends_pc = entrypoint;
+ struct disassemble_info di;
+
+ /* An arbitrary limit on the length of the prologue. If next_frame is
+ NULL this means that there was no debug info and we are called from
+ arc_skip_prologue; otherwise, if we know the frame, we can find the
+ pc within the function.
+
+ N.B. that pc will usually be after the end of the prologue, but
+ it could actually be within the prologue (i.e. execution has
+ halted within the prologue, e.g. at a breakpoint); in that
+ case, do NOT go beyond that pc, as the instructions at the
+ pc and after have not been executed yet, so have had no effect! */
+ CORE_ADDR final_pc = (next_frame) ? frame_pc_unwind(next_frame)
+ : entrypoint + MAX_PROLOGUE_LENGTH;
+
+ if (info)
+ {
+ /* Assume that the function is a leaf function until we find out
+ that it is not (i.e. when we find the 'push blink' instruction
+ in the prologue). */
+ info->is_leaf = TRUE;
- /* See what the symbol table says. */
+ /* No registers known to be saved, as yet. */
+ info->saved_regs_mask = 0;
+ }
- if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end))
- {
- struct symbol *sym;
+ /* Initializations to use the opcodes library. */
+ arc_initialize_disassembler(&di);
+
+ DEBUG("Prologue PC: %lx\n", (unsigned long) prologue_ends_pc);
+ DEBUG("Final PC: %lx\n", (unsigned long) final_pc);
- /* Found a function. */
- sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL, NULL);
- if (sym && SYMBOL_LANGUAGE (sym) != language_asm)
+ /* Look at each instruction in the prologue. */
+ while (prologue_ends_pc < final_pc)
{
- /* Don't use this trick for assembly source files. */
- sal = find_pc_line (func_addr, 0);
- if ((sal.line != 0) && (sal.end < func_end))
- return sal.end;
+ struct arcDisState current_instr = arcAnalyzeInstr(prologue_ends_pc, &di);
+
+ printInsnState(current_instr);
+
+ /* If this instruction is in the prologue, fields in the info will be updated,
+ and the saved registers mask may be updated. */
+ if (!is_in_prologue(info, &current_instr))
+ {
+ /* Found a instruction that is not in the prologue. */
+ DEBUG("End of Prologue reached \n");
+ break;
+ }
+
+ prologue_ends_pc += current_instr.instructionLen;
}
- }
-
-#ifdef ARC4_JTAG
- skip_pc = a4_scan_prologue(pc, NULL, NULL);
-#else
- skip_pc = arc_scan_prologue(pc,NULL,NULL);
-#endif
- return skip_pc; /* End of prologue */
- //#endif
-}
+ /* Means we were not called from arc_skip_prologue. */
+ if (!((next_frame == NULL) && (info == NULL)))
+ {
+ printFrameInfo("after prologue", info, FALSE);
-/* Breakpoint from pc. Return whatever is in the tdep
- * structure. The tdep structure is changed depending
- * on the correct target / architecture chosen.
- */
+ find_previous_stack_pointer(info, next_frame);
-static const unsigned char *
-arc_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
-{
+ /* The PC is found in blink (the actual register or located on the stack). */
+ info->saved_regs[ARC_PC_REGNUM] = info->saved_regs[ARC_BLINK_REGNUM];
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
- *lenptr = tdep->arc_breakpoint_size;
- return tdep->arc_breakpoint_insn;
+ printFrameInfo("after previous SP found", info, TRUE);
+ }
+
+ return prologue_ends_pc;
+ }
}
-/* Assuming NEXT_FRAME->prev is a dummy, return the frame ID of that
- dummy frame. The frame ID's base needs to match the TOS value
- saved by save_dummy_frame_tos(), and the PC match the dummy frame's
- breakpoint. */
+/* -------------------------------------------------------------------------- */
+/* local functions for handling function return values */
+/* -------------------------------------------------------------------------- */
-static struct frame_id
-arc_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
-{
- return frame_id_build (arc_unwind_sp (gdbarch, next_frame),
- frame_pc_unwind (next_frame));
-}
+/* This function gets the return value of a function from the registers used to
+ return it, according to the convention used by the ABI.
+
+ Parameters:
+ type : the information for the return type of the function
+ regcache : the register cache holding the register contents
+ valbuf : a buffer to be filled with the return value
+*/
+
+static void
+extract_return_value (struct type *type,
+ struct regcache *regcache,
+ gdb_byte *valbuf)
-/* The workhorse : frame_unwind_cache for the ARC700 platform .
- */
-static struct arc_unwind_cache *
-arc_frame_unwind_cache (struct frame_info *next_frame,
- void **this_prologue_cache)
{
- //#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_frame_unwind_cache called\n ");
-#endif
- //#else
- CORE_ADDR pc;
- struct arc_unwind_cache *info;
- int i;
-
-
- if ((*this_prologue_cache))
- return (*this_prologue_cache);
-
- info = FRAME_OBSTACK_ZALLOC (struct arc_unwind_cache);
- (*this_prologue_cache) = info;
- info->saved_regs = trad_frame_alloc_saved_regs (next_frame);
-
- /* Zero all fields. */
- info->blink_offset = 0;
- info->prev_pc = 0;
- info->prev_sp = 0;
- info->frame_base = 0;
- info->framesize = 0;
- info->sp_offset = 0;
- info->fp_offset = 0;
- info->prev_pc = 0;
- info->is_leaf = 0;
- info->uses_fp = 0;
-
- /* Prologue analysis does the rest... */
- /* Currently our scan prologue does not
- * support getting input for the frame unwinder
- */
-
- pc = frame_func_unwind(next_frame);
-#ifdef ARC4_JTAG
- a4_scan_prologue (pc, next_frame, info);
-#else
- arc_scan_prologue (pc, next_frame, info);
-#endif
+ unsigned int len = TYPE_LENGTH (type);
+
+ ENTERMSG;
+
+ if (len <= BYTES_IN_REGISTER)
+ {
+ ULONGEST val;
+
+ /* Get the return value from one register. */
+ regcache_cooked_read_unsigned (regcache, ARC_ABI_RETURN_REGNUM, &val);
+ store_unsigned_integer (valbuf, (int) len, val);
+
+ DEBUG("returning 0x%08lX\n", (unsigned long) val);
+ }
+ else if (len <= BYTES_IN_REGISTER * 2)
+ {
+ ULONGEST low, high;
+
+ /* Get the return value from two registers. */
+ regcache_cooked_read_unsigned (regcache, ARC_ABI_RETURN_LOW_REGNUM, &low);
+ regcache_cooked_read_unsigned (regcache, ARC_ABI_RETURN_HIGH_REGNUM, &high);
- return info;
- //#endif
+ store_unsigned_integer (valbuf, BYTES_IN_REGISTER, low);
+ store_unsigned_integer (valbuf + BYTES_IN_REGISTER, (int) len - BYTES_IN_REGISTER, high);
+
+ DEBUG("returning 0x%08lX%08lX\n",
+ (unsigned long) high, (unsigned long) low);
+ }
+ else
+ error(_("%s: type length %u too large"), __FUNCTION__, len);
}
+/* This function loads the return value of a function into the registers used to
+ return it, according to the convention used by the ABI.
-/*
- * Construct frame id for the normal frame
- */
+ Parameters:
+ type : the information for the return type of the function
+ regcache : the register cache holding the register contents
+ valbuf : a buffer holding the return value
+*/
static void
-arc_frame_this_id (struct frame_info *next_frame,
- void **this_prologue_cache,
- struct frame_id *this_id)
+store_return_value (struct type *type,
+ struct regcache *regcache,
+ const gdb_byte *valbuf)
{
- // FIXMEA: cleanup #ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\n arc_frame_this_id called()\n ");
-#endif
- //#else
+ unsigned int len = TYPE_LENGTH (type);
+
+ ENTERMSG;
- struct arc_unwind_cache *info
- = arc_frame_unwind_cache (next_frame, this_prologue_cache);
- CORE_ADDR base;
- CORE_ADDR func;
- struct frame_id id;
+ if (len <= BYTES_IN_REGISTER)
+ {
+ ULONGEST val;
- /* The FUNC is easy. */
- func = frame_func_unwind (next_frame);
+ /* Put the return value into one register. */
+ val = extract_unsigned_integer (valbuf, (int) len);
+ regcache_cooked_write_unsigned (regcache, ARC_ABI_RETURN_REGNUM, val);
- /* This is meant to halt the backtrace at the entry point (_start). */
- if (func <= gdbarch_tdep (current_gdbarch)->lowest_pc)
- return;
-
- /* Hopefully the prologue analysis either correctly determined the
- frame's base (which is the SP from the previous frame), or set
- that base to "NULL". */
- base = info->prev_sp;
- if (base == 0)
- return;
+ DEBUG("storing 0x%08lX\n", (unsigned long) val);
+ }
+ else if (len <= BYTES_IN_REGISTER * 2)
+ {
+ ULONGEST low, high;
- id = frame_id_build (base, func);
+ /* Put the return value into two registers. */
+ low = extract_unsigned_integer (valbuf, BYTES_IN_REGISTER);
+ high = extract_unsigned_integer (valbuf + BYTES_IN_REGISTER, (int) len - BYTES_IN_REGISTER);
- (*this_id) = id;
- //#endif
+ regcache_cooked_write_unsigned (regcache, ARC_ABI_RETURN_LOW_REGNUM, low);
+ regcache_cooked_write_unsigned (regcache, ARC_ABI_RETURN_HIGH_REGNUM, high);
+ DEBUG("storing 0x%08lX%08lX\n",
+ (unsigned long) high, (unsigned long) low);
+ }
+ else
+ error(_("arc_store_return_value: type length too large."));
}
-/*
- * Unwind and obtain the register information
- */
-static void
-arc_frame_prev_register (struct frame_info *next_frame,
- void **this_prologue_cache,
- int regnum, int *optimizedp,
- enum lval_type *lvalp, CORE_ADDR *addrp,
- int *realnump, void *bufferp)
+/* -------------------------------------------------------------------------- */
+/* local functions for handling the stack frame */
+/* -------------------------------------------------------------------------- */
+
+/* This is copied from file stack.c in the gdb sources.
+ It identifies a frame from information (such as frame number) given by the
+ user (in the frame_exp parameter). */
+
+static struct frame_info*
+parse_frame_specification_1 (const char *frame_exp,
+ const char *message,
+ int *selected_frame_p)
{
- // FIXMEA:
- //#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\n arc_frame_prev_register() called for regnum %d\n ",regnum );
-#endif
- //#else
- struct arc_unwind_cache *info
- = arc_frame_unwind_cache (next_frame, this_prologue_cache);
+ int numargs = 0;
+ struct value *args[4];
+ CORE_ADDR addrs[ARRAY_SIZE (args)];
+ if (frame_exp)
+ {
+ while (TRUE)
+ {
+ char *addr_string;
+ struct cleanup *cleanup;
+ const char *p;
- /* If we are asked to unwind the PC, then we need to return blink
- instead. The saved value of PC points into this frame's
- prologue, not the next frame's resume location. */
-#ifdef ARC4_JTAG
- if (regnum == ARC_STATUS_REGNUM)
-#else
- if (regnum == PC_REGNUM)
-#endif
- regnum = ARC_BLINK_REGNUM;
+ /* Skip leading white space. */
+ while (isspace (*frame_exp))
+ frame_exp++;
+
+ if (*frame_exp == '\0')
+ break;
+
+ /* Parse the argument, extract it, save it. */
+ for (p = frame_exp; (*p != '\0') && !isspace (*p); p++);
+
+ addr_string = savestring (frame_exp, (size_t) (p - frame_exp));
+ frame_exp = p;
+ cleanup = make_cleanup (xfree, addr_string);
+
+ /* NOTE: Parse and evaluate expression, but do not use
+ functions such as parse_and_eval_long or
+ parse_and_eval_address to also extract the value.
+ Instead value_as_long and value_as_address are used.
+ This avoids problems with expressions that contain
+ side-effects. */
+ if (numargs >= (int) ARRAY_SIZE (args))
+ error (_("Too many args in frame specification"));
+
+ args[numargs++] = parse_and_eval (addr_string);
+
+ do_cleanups (cleanup);
+ }
+ }
- /* SP is generally not saved to the stack, but this frame is
- identified by NEXT_FRAME's stack pointer at the time of the call.
- The value was already reconstructed into PREV_SP. */
- if (regnum == ARC_SP_REGNUM)
+ /* If no args, default to the selected frame. */
+ if (numargs == 0)
{
- *lvalp = not_lval;
- if (bufferp)
- store_unsigned_integer (bufferp, 4, info->prev_sp);
- return;
+ if (selected_frame_p != NULL)
+ (*selected_frame_p) = 1;
+ return get_selected_frame (message);
}
+ /* None of the remaining use the selected frame. */
+ if (selected_frame_p != NULL)
+ (*selected_frame_p) = 0;
- trad_frame_get_prev_register (next_frame, info->saved_regs, regnum,
- optimizedp, lvalp, addrp, realnump, bufferp);
+ /* Assume the single arg[0] is an integer, and try using that to
+ select a frame relative to current. */
+ if (numargs == 1)
+ {
+ int level = (int) value_as_long (args[0]);
+ struct frame_info *fid = find_relative_frame (get_current_frame (), &level);
+ if (level == 0)
+ /* find_relative_frame was successful. */
+ return fid;
+ }
-#ifdef ARC_DEBUG
- printf("-*-*-*\n Regnum =%d, realnump=%d,%d \n",regnum, (char *)(bufferp), *((char*)bufferp));
-#endif
- //#endif
+ /* Convert each value into a corresponding address. */
+ {
+ int i;
+ for (i = 0; i < numargs; i++)
+ addrs[i] = value_as_address (args[i]);
+ }
+
+ /* Assume that the single arg[0] is an address, use that to identify
+ a frame with a matching ID. Should this also accept stack/pc or
+ stack/pc/special. */
+ if (numargs == 1)
+ {
+ struct frame_id id = frame_id_build_wild (addrs[0]);
+ struct frame_info *fid;
+
+ /* If (s)he specifies the frame with an address, he deserves
+ what (s)he gets. Still, give the highest one that matches.
+ (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+ know). */
+ for (fid = get_current_frame ();
+ fid != NULL;
+ fid = get_prev_frame (fid))
+ {
+ if (frame_id_eq (id, get_frame_id (fid)))
+ {
+ while (frame_id_eq (id, frame_unwind_id (fid)))
+ fid = get_prev_frame (fid);
+ return fid;
+ }
+ }
+ }
+
+ /* We couldn't identify the frame as an existing frame, but
+ perhaps we can create one with a single argument. */
+ if (numargs == 1)
+ return create_new_frame (addrs[0], 0);
+ else if (numargs == 2)
+ return create_new_frame (addrs[0], addrs[1]);
+ else
+ error (_("Too many args in frame specification"));
}
+/* Return an array (and count) of the local variables and parameters declared
+ in the function which owns the given frame.
+ Parameters:
+ frame : the frame information for the function
+ variables: a pointer to an existing array of variable information (may be NULL)
+ count : (IN/OUT) the total number of variables found
+ callee : TRUE if the function is a callee of another function
-static const struct frame_unwind arc_frame_unwind = {
- NORMAL_FRAME,
- arc_frame_this_id,
- arc_frame_prev_register
-};
+ Result:
+ A pointer to an array containing one element for each parameter, and, if
+ 'callee' is FALSE, each local variable, of the function. */
-const struct frame_unwind *
-arc_frame_sniffer (struct frame_info *next_frame)
+static LocalVariable*
+find_variables (struct frame_info *frame,
+ LocalVariable *variables,
+ unsigned int *count,
+ Boolean callee)
{
- return &arc_frame_unwind;
+ struct block *block = get_frame_block (frame, 0);
+ unsigned int vars = *count;
+
+ while (block)
+ {
+ struct dict_iterator iter;
+ struct symbol *sym;
+
+ ALL_BLOCK_SYMBOLS (block, iter, sym)
+ {
+ Boolean is_arg = (SYMBOL_CLASS(sym) == LOC_COMPUTED_ARG);
+ CORE_ADDR addr;
+
+ switch (SYMBOL_CLASS (sym))
+ {
+ case LOC_COMPUTED:
+ case LOC_COMPUTED_ARG:
+ addr = SYMBOL_OPS (sym)->get_variable_address(sym, frame);
+
+ /* For callees, we are only interested in the arguments. */
+ if ((addr != 0) && (!callee || is_arg))
+ {
+ struct type *type = SYMBOL_TYPE (sym);
+
+ vars++;
+ variables = xrealloc(variables, sizeof(LocalVariable) * vars);
+
+ if (variables)
+ {
+ LocalVariable *var = &variables[vars - 1];
+
+ var->name = SYMBOL_PRINT_NAME (sym);
+ var->address = addr;
+ var->is_callee = callee;
+ var->is_argument = is_arg;
+ var->is_array = (TYPE_CODE (type) == TYPE_CODE_ARRAY);
+ var->size = SYMBOL_OPS (sym)->get_variable_size(sym, frame);
+
+ if (var->is_array)
+ var->element_size = TYPE_LENGTH (TYPE_TARGET_TYPE (type));
+ }
+ else
+ {
+ *count = 0;
+ return NULL;
+ }
+ }
+ break;
+
+ default:
+ /* Ignore symbols which are not locals. */
+ break;
+ }
+ }
+
+ /* After handling the function's top-level block, stop. Don't continue
+ to its superblock, the block of per-file symbols. */
+ if (BLOCK_FUNCTION (block))
+ break;
+ block = BLOCK_SUPERBLOCK (block);
+ }
+
+ *count = vars;
+ return variables;
}
-/* read-only registers */
-static int
-arc_cannot_store_register (int regno)
+/* Return an array (and count) of the local variables declared in the function
+ which owns the given frame, and also those declared in the function which is
+ the callee (if any) of that function in the current call chain. */
+
+static LocalVariable*
+find_local_variables (struct frame_info *frame, unsigned int *count)
{
- if(
-#ifndef ARC4_JTAG
- regno == ARC_EFA_REGNUM ||
- regno == ARC_ERET_REGNUM ||
- regno == ARC_STATUS32_L1_REGNUM ||
- regno == ARC_STATUS32_L2_REGNUM ||
- regno == ARC_ERSTATUS_REGNUM ||
-#endif
- regno == ARC_ILINK1_REGNUM ||
- regno == ARC_ILINK2_REGNUM
- )
+ struct frame_info *callee = get_next_frame(frame);
+ LocalVariable *variables;
+
+ *count = 0;
+
+ variables = find_variables(frame, NULL, count, FALSE);
+
+ if (callee)
+ variables = find_variables(callee, variables, count, TRUE);
+
+ return variables;
+}
+
+
+/* Try to add the name of a local variable or function parameter to a line of
+ output, if a given address lies within the range of locations occupied by
+ that data item.
+
+ Parameters:
+ line : the line in which any output is to be placed
+ location : the address of a location on the stack
+ variables : an array of local variables and parameters
+ num_variables : the number of elements in the array
+ is_following_element : set to TRUE if the location lies within an
+ array element, and it is not the 0th element
+ elements_are_word_sized: set to TRUE if the array element is word-sized
+
+ Returns TRUE if the given location holds a local variable or parameter
+ (i.e. information has been added to the line of output). */
+
+static Boolean
+add_local_name (char *line,
+ CORE_ADDR location,
+ LocalVariable *variables,
+ unsigned int num_variables,
+ Boolean *is_following_element,
+ Boolean *elements_are_word_sized)
+{
+ unsigned int i;
+
+ *is_following_element = FALSE;
+ *elements_are_word_sized = FALSE;
+
+ /* Look at each of the local variables / parameters in the array. */
+ for (i = 0; i < num_variables; i++)
{
- /* No warning should be printed. arc_cannot_store_register being
- called does not imply that someone is actually writing to regnum. */
+ LocalVariable *var = &variables[i];
+ int index = -1;
- /*warning("writing to read-only register: %s\n", arc_register_name(regno));*/
- return 1;
- }
- return 0;
+ /* is the variable an array? */
+ if (var->is_array)
+ {
+ /* If we know the size of the array, and the size of its elements. */
+ if (var->size > 0 && var->element_size > 0)
+ {
+ /* What is the offset of the given stack location from the start
+ of the array? */
+ int offset = (int) ((long int) location - (long int) var->address);
+
+ /* Does that offset actually lie within the array? */
+ if (0 <= offset && offset < (int) var->size)
+ {
+ /* Compute the index of the array element which contains the
+ location. */
+ index = offset / var->element_size;
+ }
+
+ if (var->element_size == BYTES_IN_WORD)
+ *elements_are_word_sized = FALSE;
+ }
+ }
+
+ /* If the variable starts in the given location, or the variable is an
+ array and one of its elements contains the location. */
+ if (var->address == location || index >= 0)
+ {
+ int n;
+
+ /* What is the variable? */
+ if (var->is_callee)
+ n = sprintf(line, _("callee parameter"));
+ else if (var->is_argument)
+ n = sprintf(line, _("parameter"));
+ else
+ n = sprintf(line, _("local variable"));
+
+ line[n] = ' ';
+ n++;
+ n += sprintf(line + n, _("'%s'"), var->name);
+ line[n] = ' ';
+
+ /* If it is an array element. */
+ if (index >= 0)
+ {
+ /* Add the array element index to the output. */
+ (void) sprintf(line + n, _("[%u]"), index);
+
+ /* Is it an element which follows another element? */
+ *is_following_element = (index > 0);
+ }
+ else if (var->size > BYTES_IN_WORD)
+ {
+ /* It occupies more than one word. */
+ (void) sprintf(line + n + 1, _("(%u words)"),
+ WORDS_OCCUPIED(var->size));
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
}
-/* Returns true if the insn at PC is a branch. *fall_thru is the address of
- the next insn. *target is the branch target. */
-static int
-arc_next_pc(CORE_ADDR pc, CORE_ADDR *fall_thru, CORE_ADDR *target)
+
+/* Try to identify the given frame, and output that identification. */
+
+static void
+identify_frame (struct frame_info *frame)
{
-#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_next_pc called\n");
-#endif
-#else
- struct arcDisState instr, instr_d;
- int insn_length;
- struct disassemble_info di;
- int two_targets = 0;
-
- init_disassemble_info(&di, NULL, NULL);
- di.arch = gdbarch_bfd_arch_info(current_gdbarch)->arch;
- di.mach = gdbarch_bfd_arch_info(current_gdbarch)->mach;
- di.endian = gdbarch_byte_order(current_gdbarch);
- di.read_memory_func = target_read_memory;
-
- instr = arcAnalyzeInstr(pc, &di);
-
- *fall_thru = pc + instr.instructionLen;
-
-#ifdef ARC_DEBUG
- printf("--- arc_next_pc(%x) = %x, isBranch = %d, tcnt = %d [%x], flow = %s (%d), "
- "reg for indirect jump = %d, nullifyMode = %s\n",
- pc, *fall_thru, instr.isBranch, instr.tcnt, instr.targets[0],
- (instr.flow == direct_jump || instr.flow == direct_call) ? "direct" : "indirect",
- instr.flow,
- instr.register_for_indirect_jump,
- (instr.nullifyMode == BR_exec_always ? "delay slot" : "no delay"));
-#endif
+ enum language func_lang = language_unknown;
+ char *func_name = NULL;
+ char *demangled = NULL;
+ struct symbol *func;
+ struct symtab_and_line sal;
- if(instr.isBranch)
+ find_frame_sal (frame, &sal);
+ func = get_frame_function (frame);
+
+ /* Have we found the function owning the frame? */
+ if (func)
{
- two_targets = 1;
-
- if(instr.flow == direct_jump || instr.flow == direct_call)
- *target = instr.targets[0];
- else
- regcache_cooked_read(current_regcache,
- arc_binutils_reg_to_regnum(instr.register_for_indirect_jump),
- target);
+ func_name = DEPRECATED_SYMBOL_NAME (func);
+ func_lang = SYMBOL_LANGUAGE (func);
}
+ else
+ {
+ struct minimal_symbol *msymbol;
- /* for instructions with delay slots, the fall thru is not the instruction
- immediately after the branch instruction, but the one after that */
- if(instr.isBranch && instr.nullifyMode == BR_exec_always)
+ /* Try to find the symbol most closely associated with the PC
+ corresponding to the frame. */
+ msymbol = lookup_minimal_symbol_by_pc (get_frame_pc (frame));
+
+ if (msymbol != NULL)
+ {
+ func_name = DEPRECATED_SYMBOL_NAME (msymbol);
+ func_lang = SYMBOL_LANGUAGE (msymbol);
+ }
+ }
+
+ /* Have we found a name? */
+ if (func_name)
{
- instr_d = arcAnalyzeInstr(*fall_thru, &di);
- *fall_thru += instr_d.instructionLen;
+ /* If user wants to see raw output, no problem.
+ (demangle is a global flag which can be set by user command). */
+ if (demangle)
+ {
+ demangled = language_demangle (language_def (func_lang),
+ func_name,
+ DMGL_ANSI | DMGL_PARAMS);
+
+ /* If the demangler fails, try the demangled name from the symbol
+ table. That'll have parameters, but that's preferable to
+ displaying a mangled name. */
+ if (demangled == NULL)
+ {
+ if (func == NULL)
+ func_name = _("<unknown function>");
+ else
+ func_name = SYMBOL_PRINT_NAME (func);
+ }
+ else
+ func_name = demangled;
+ }
}
+ else
+ func_name = _("<unknown function>");
- /* zero-overhead loops:
- if(status32[L] == 0 && next_pc == lp_end && lp_count > 1)
- next_pc = lp_start;
- */
- {
- unsigned int lp_end, lp_start, lp_count, status32;
-
- regcache_cooked_read(current_regcache, ARC_LP_START_REGNUM, &lp_start);
- regcache_cooked_read(current_regcache, ARC_LP_END_REGNUM, &lp_end);
- regcache_cooked_read(current_regcache, ARC_LP_COUNT_REGNUM, &lp_count);
-#ifndef ARC4_JTAG
- regcache_cooked_read(current_regcache, ARC_STATUS32_REGNUM, &status32);
-#endif
+ printf_filtered(_("Frame of function: %s"), func_name);
+ if (sal.symtab)
+ printf_filtered(_(" (%s:%d)"), sal.symtab->filename, sal.line);
+ printf_filtered(NEW_LINE);
- if( !(status32 & ARC_STATUS32_L) && *fall_thru == lp_end && lp_count > 1)
- {
- two_targets = 1;
- *target = lp_start;
- }
- }
-
- return two_targets;
-#endif //
+ if (demangled != NULL)
+ xfree (demangled);
}
-/* this is called with insert_breakpoints_p = 1 before single-stepping and
- with insert_breakpoints_p = 0 after the step */
-void
-arc_software_single_step(enum target_signal ignore, int insert_breakpoints_p)
+
+/* -------------------------------------------------------------------------- */
+/* local functions called from gdb */
+/* -------------------------------------------------------------------------- */
+
+/* Standard register type for the ARC platform.
+ It would be builtin_type_uint32 until we consider the DSP extensions. */
+
+static struct type *
+arc_register_type (struct gdbarch *gdbarch, int regnum)
{
-#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_software_single_step called\n" );
-#endif
-#else
- typedef char binsn_quantum[BREAKPOINT_MAX];
-
- static CORE_ADDR fall_thru, branch_target;
- static binsn_quantum break_mem[2];
- static char two_breakpoints;
- CORE_ADDR pc;
-
- {
-#ifdef ARC_DEBUG
- unsigned int efa, ret;
- regcache_cooked_read(current_regcache, ARC_EFA_REGNUM, &efa);
- // regcache_cooked_read(current_regcache, ARC_RET_REGNUM, &ret);
-
- printf("--- arc_software_single_step, efa = %x, ret = %x, (%s)\n", efa, ret,
- (insert_breakpoints_p ? "add" : "remove"));
-#endif
- }
-
- if (insert_breakpoints_p)
- {
- pc = read_pc ();
- two_breakpoints = arc_next_pc (pc, &fall_thru, &branch_target);
-
- if (two_breakpoints && branch_target == pc)
- {
- warning ("Cannot single-step branch-to-self or single instruction zero overhead loop,\n"
- " Stepping across it.");
- /* Don't insert/remove the branch-target breakpoint. */
- two_breakpoints = 0;
- }
-
- target_insert_breakpoint (fall_thru, break_mem[0]);
- if(two_breakpoints)
- target_insert_breakpoint (branch_target, break_mem[1]);
- }
- else
+ return builtin_type_uint32;
+}
+
+
+/* Skip the prologue for the function at pc.
+ Returns the address of the first instruction after the prologue. */
+
+static CORE_ADDR
+arc_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+ CORE_ADDR function_addr, function_end = 0;
+ char *function_name;
+
+ /* This is done by checking from the line information read from the DWARF,
+ if possible; otherwise, we scan the function prologue to find its end. */
+
+ ENTERMSG;
+
+ /* If we're in a dummy frame, don't even try to skip the prologue. */
+ if (deprecated_pc_in_call_dummy (pc))
+ return pc;
+
+ /* See what the symbol table says. */
+ if (find_pc_partial_function (pc, &function_name, &function_addr, &function_end))
{
- target_remove_breakpoint (fall_thru, break_mem[0]);
- if(two_breakpoints)
- target_remove_breakpoint (branch_target, break_mem[1]);
+ /* Found a function. */
+ struct symbol *sym = lookup_symbol (function_name, NULL, VAR_DOMAIN, NULL, NULL);
+
+ if ((sym != NULL) && SYMBOL_LANGUAGE (sym) != language_asm)
+ {
+ /* Don't use this trick for assembly source files. */
+ struct symtab_and_line sal = find_pc_line (function_addr, 0);
+
+ if ((sal.line != 0) && (sal.end < function_end))
+ return sal.end;
+ }
}
-#endif
+
+ /* Find the address of the first instruction after the prologue by scanning
+ through it - no other information is needed, so pass NULL for the other
+ parameters. */
+ return scan_prologue(pc, NULL, NULL);
}
-/*
- * mapping from binutils/gcc register number to
- * GDB register number ("regnum")
- */
-static int
-arc_binutils_reg_to_regnum (int reg)
+
+/* Construct frame id for the normal frame. */
+
+static void
+arc_frame_this_id (struct frame_info *next_frame,
+ void **this_prologue_cache,
+ struct frame_id *this_id)
{
-#ifdef ARC4_JTAG
- if (reg >= 0 && reg <= 26)
- return reg;
- else if (reg == 27) /* fp */
- return ARC_FP_REGNUM;
- else if (reg == 28) /* sp */
- return ARC_SP_REGNUM;
- else if (reg == 29) /* ilink1 */
- return ARC_ILINK1_REGNUM;
- else if (reg == 30) /* ilink2 */
- return ARC_ILINK2_REGNUM;
- else if (reg == 31) /* blink */
- return ARC_BLINK_REGNUM;
+ ENTERMSG;
-#else
- /* from gcc/config/arc/arc.h */
-
- if (reg >= 0 && reg <= 26)
- return reg;
- else if (reg == 27) /* fp */
- return ARC_FP_REGNUM;
- else if (reg == 28) /* sp */
- return ARC_SP_REGNUM;
- else if (reg == 29) /* ilink1 */
- return ARC_ILINK1_REGNUM;
- else if (reg == 30) /* ilink2 */
- return ARC_ILINK2_REGNUM;
- else if (reg == 31) /* blink */
- return ARC_BLINK_REGNUM;
- else if (reg >= 32 && reg <= 59) /* reserved */
- ;
- else if (reg == 60) /* lp_count */
- return ARC_LP_COUNT_REGNUM;
- else if (reg == 61) /* reserved */
- ;
- else if (reg == 62) /* no such register */
- ;
-/* else if (reg == 63) /\* PCL *\/ */
-/* return ARC_RET_REGNUM; */
+ /* FIXME: to what should *this_id be set if the frame base can not be found? */
+
+ {
+ /* Find the entry point of the function which owns the frame. */
+ CORE_ADDR entrypoint = frame_func_unwind (next_frame, NORMAL_FRAME);
+
+ /* This is meant to halt the backtrace at the entry point (_start)
+ (it assumes that there is no code at a lower address). */
+ if (entrypoint > gdbarch_tdep (current_gdbarch)->lowest_pc)
+ {
+ UnwindCache *info = frame_unwind_cache (next_frame, this_prologue_cache);
+ CORE_ADDR base = info->prev_sp;
+
+ /* Hopefully the prologue analysis either correctly determined the
+ frame's base (which is the SP from the previous frame), or set
+ that base to "NULL". */
+ if (base != 0)
+ {
+ /* Build the ID from the frame base address. */
+ *this_id = frame_id_build (base, entrypoint);
+
+#if 0
+ printf("*** Frame ID: %x ==> (%x %x %x)\n",
+ base,
+ this_id->stack_addr,
+ this_id->code_addr,
+ this_id->special_addr);
#endif
- warning ("Unmapped register #%d encountered\n", reg);
- return -1;
+ }
+ }
+ }
}
+/* Unwind and obtain the register information. */
+
static void
-arc_add_reggroups (struct gdbarch *gdbarch)
+arc_frame_prev_register (struct frame_info *next_frame,
+ void **this_prologue_cache,
+ int regnum,
+ int *optimized,
+ enum lval_type *lval,
+ CORE_ADDR *addr,
+ int *realnum,
+ gdb_byte *buffer)
{
- reggroup_add (gdbarch, general_reggroup);
- reggroup_add (gdbarch, all_reggroup);
- reggroup_add (gdbarch, system_reggroup);
+ ENTERARGS("regnum %d ", regnum);
+
+ {
+ UnwindCache *info = frame_unwind_cache (next_frame, this_prologue_cache);
+
+ /* If we are asked to unwind the PC, then we need to return blink
+ instead: the saved value of PC points into this frame's function's prologue,
+ not the next frame's function's resume location. */
+ if (regnum == ARC_PC_REGNUM)
+ regnum = ARC_BLINK_REGNUM;
+
+ /* SP is generally not saved to the stack, but this frame is identified
+ by next_frame's stack pointer at the time of the call. The value was
+ already reconstructed into prev_sp. */
+ if (regnum == ARC_SP_REGNUM)
+ {
+ /* This value is not an L-value, i.e. it can not be changed, because
+ it is implicit in the structure of the call-chain. */
+ *lval = not_lval;
+
+ if (buffer)
+ store_unsigned_integer (buffer, BYTES_IN_REGISTER, info->prev_sp);
+ return;
+ }
+
+ trad_frame_get_prev_register (next_frame,
+ info->saved_regs,
+ regnum,
+ optimized,
+ lval,
+ addr,
+ realnum,
+ buffer);
+
+ DEBUG("-*-*-*\n Regnum = %d, buffer = %p\n", regnum, buffer);
+ }
}
-int
-arc_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
- struct reggroup *group)
+
+/* This function is passed to gdb to enable it to unwind frames. */
+
+static const struct frame_unwind *
+arc_frame_sniffer (struct frame_info *next_frame)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
- int tdep_answer;
-
- tdep_answer = tdep->register_reggroup_p (regnum, group);
- if(tdep_answer != -1)
- return tdep_answer;
-
- if (group == all_reggroup)
- return 1;
- else if (group == save_reggroup || group == restore_reggroup)
+ static const struct frame_unwind arc_frame_unwind =
{
- /* don't save/restore read-only registers. */
- return (!arc_cannot_store_register(regnum));
- }
- else if (group == general_reggroup)
+ NORMAL_FRAME, // type
+ arc_frame_this_id, // this_id
+ arc_frame_prev_register, // prev_register
+ NULL, // unwind_data
+ NULL, // sniffer
+ NULL, // prev_pc
+ NULL // dealloc_cache
+ };
+
+ return &arc_frame_unwind;
+}
+
+
+/* Get the breakpoint which is appropriate for address at which it is to be set.
+
+ Return whatever is in the ARC-private tdep structure (this has been set up
+ according to the correct target / architecture chosen).
+
+ Fortunately, the ARC processor does not have separate instruction sets (like
+ the ARM's normal 32-bit and 16-bit Thumb instructions), so the bp instruction
+ to be used does not depend on the address (although the ARC does have both
+ 16- and 32-bit instructions, they may be freely intermixed). */
+
+static const unsigned char *
+arc_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int end = gdbarch_byte_order (gdbarch);
+
+ /* Return the breakpoint instruction length. */
+ *lenptr = (int) tdep->breakpoint_size;
+
+ /* Return the breakpoint instruction code. */
+ if (end == BFD_ENDIAN_LITTLE)
+ return tdep->le_breakpoint_instruction;
+ if (end == BFD_ENDIAN_BIG)
+ return tdep->be_breakpoint_instruction;
+
+ internal_error (__FILE__, __LINE__, "target endianness is not known");
+}
+
+
+/* Determine whether the given register is a member of the given group.
+
+ Returns 0, 1, or -1:
+ 0 means the register is not in the group.
+ 1 means the register is in the group.
+ -1 means the tdep has nothing to say about this register and group. */
+
+static int
+arc_register_reggroup_p (struct gdbarch *gdbarch,
+ int regnum,
+ struct reggroup *group)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int tdep_answer = tdep->register_reggroup_p (regnum, group);
+
+ /* If the configuration-specific tdep knows about this register. */
+ if (tdep_answer != -1)
+ return tdep_answer;
+
+ if (group == all_reggroup)
+ return 1;
+
+ if (group == save_reggroup || group == restore_reggroup)
{
-#ifndef ARC4_JTAG
- if (regnum == ARC_STATUS32_REGNUM)
- return 0;
-#endif
- return 1;
+ /* Don't save/restore read-only registers. */
+ return (!gdbarch_cannot_store_register(current_gdbarch, regnum));
}
- else
+
+ if (group == system_reggroup)
{
- internal_error(__FILE__, __LINE__, "bad register group");
+ if (regnum == ARC_ILINK1_REGNUM ||
+ regnum == ARC_ILINK2_REGNUM)
+ return 1;
+
+ return 0;
}
+
+ internal_error(__FILE__, __LINE__, _("bad register group"));
+ return 0;
}
+/* This function is used by the DWARF-2 frame sniffer.
+ It is the architecture-specific register state initialization function:
+ it tells gdb how to find certain registers in the DWARF-2 Call Frame Information. */
static void
-arc_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
- struct dwarf2_frame_state_reg *reg)
+arc_dwarf2_frame_init_reg (struct gdbarch *gdbarch,
+ int regnum,
+ struct dwarf2_frame_state_reg *reg,
+ struct frame_info *info)
{
-#ifdef ARC4_JTAG
- // FIXMEA: Clean up. if ( debug_arc_jtag_target_message)
-#ifdef ARC_DEBUG
- printf ("\n arc_dwarf2_frame_init_reg called.\n Regno no:%d,0x%x\n",regnum,regnum);
-#endif
+// ENTERARGS("Regno no:%d, 0x%x", regnum, (unsigned int) regnum);
+
+ /* Make sure that we know the number of the PC! */
+ arc_check_pc_defined(gdbarch);
+
/* The return address column. */
- if (regnum == ARC_STATUS_REGNUM)
- reg->how = DWARF2_FRAME_REG_RA;
-
- /* The call frame address. */
- if (regnum == ARC_SP_REGNUM)
- reg->how = DWARF2_FRAME_REG_CFA;
+ if (regnum == ARC_PC_REGNUM)
+ reg->how = DWARF2_FRAME_REG_RA;
-#else
- /* The return address column. */
- if (regnum == PC_REGNUM)
- reg->how = DWARF2_FRAME_REG_RA;
-
- /* The call frame address. */
- if (regnum == ARC_SP_REGNUM)
- reg->how = DWARF2_FRAME_REG_CFA;
-#endif
+ /* The call frame address. */
+ if (regnum == ARC_SP_REGNUM)
+ reg->how = DWARF2_FRAME_REG_CFA;
}
+
+/* Unwind the frame to find the previous frame's PC. */
+
static CORE_ADDR
arc_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
- ULONGEST pc;
-#ifdef ARC4_JTAG
- frame_unwind_unsigned_register (next_frame, ARC_STATUS_REGNUM, &pc);
- pc = pc & 0x00ffffff;
- pc = pc << 2;
-#else
- frame_unwind_unsigned_register (next_frame, PC_REGNUM, &pc);
-#endif
- return pc;
+ ULONGEST pc;
+
+ /* Make sure that we know the number of the PC! */
+ arc_check_pc_defined(gdbarch);
+
+ pc = frame_unwind_register_unsigned (next_frame, ARC_PC_REGNUM);
+
+ DEBUG("unwind PC: 0x%08lx\n", (unsigned long) pc);
+
+ return (CORE_ADDR) pc;
}
+
+/* Unwind the frame to find the previous frame's SP. */
+
static CORE_ADDR
arc_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
- ULONGEST sp;
- frame_unwind_unsigned_register (next_frame, SP_REGNUM, &sp);
- return sp;
-}
+ ULONGEST sp;
+ sp = frame_unwind_register_unsigned (next_frame, ARC_SP_REGNUM);
+ DEBUG("unwind SP: 0x%08lx\n", (unsigned long) sp);
-static void
-arc_extract_return_value (struct type *type, struct regcache *regcache,
- void *valbuf)
-{
- //#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_extract_return_value called\n");
-#endif
- //#else
- ULONGEST val;
- int len = TYPE_LENGTH (type);
-
- if (len <= 4)
- {
- /* Get the return value from R0. */
- regcache_cooked_read_unsigned (regcache, ARC_RETURN1_REGNUM, &val);
- store_unsigned_integer (valbuf, len, val);
- }
- else if (len <= 8)
- {
- /* Get the return value from R0 and R1. */
- /* R0 holds the lower-order bytes */
- regcache_cooked_read_unsigned (regcache, ARC_RETURN1_REGNUM, &val);
- store_unsigned_integer (valbuf, 4, val);
- regcache_cooked_read_unsigned (regcache, ARC_RETURN2_REGNUM, &val);
- store_unsigned_integer ((char *)valbuf + 4, len - 4, val);
- }
- else
- error ("arc_extract_return_value: type length too large");
- //#endif
+ return (CORE_ADDR) sp;
}
-static void
-arc_store_return_value (struct type *type, struct regcache *regcache,
- const void *valbuf)
-{
- //#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_store_return_value called\n ");
-#endif
- //#else
- ULONGEST val;
- int len = TYPE_LENGTH (type);
-
- if (len <= 4)
- {
- /* Put the return value in R0. */
- val = extract_unsigned_integer (valbuf, len);
- regcache_cooked_write_unsigned (regcache, ARC_RETURN1_REGNUM, val);
- }
- else if (len <= 8)
- {
- /* Put the return value in R10 and R11. */
- val = extract_unsigned_integer (valbuf, 4);
- regcache_cooked_write_unsigned (regcache, ARC_RETURN1_REGNUM, val);
- val = extract_unsigned_integer ((char *)valbuf + 4, len - 4);
- regcache_cooked_write_unsigned (regcache, ARC_RETURN2_REGNUM, val);
- }
- else
- error ("arc_store_return_value: type length too large.");
- //#endif
-}
+/* This function returns the convention used by the ABI for returning a result
+ of the given type from a function; it may also be required to:
+
+ a) set the return value (this is for the situation where the debugger user
+ has issued a "return <value>" command to request immediate return from
+ the current function with the given result; or
+
+ b) get the return value ((this is for the situation where the debugger user
+ has executed a "call <function>" command to execute the specified
+ function in the target program, and that function has a non-void result
+ which must be returned to the user.
+
+ Parameters:
+ gdbarch : gdbarch structure for the backend to use if needed
+ valtype : the information for the return type of the function
+ regcache: the register cache to be used for altered register values
+ readbuf : if non-NULL, read the return value into this buffer
+ writebuf: if non-NULL, write the return value from this buffer
+ */
static enum return_value_convention
-arc_return_value (struct gdbarch *gdbarch, struct type *valtype,
- struct regcache *regcache, void *readbuf,
- const void *writebuf)
+arc_return_value (struct gdbarch *gdbarch,
+ struct type *valtype,
+ struct regcache *regcache,
+ gdb_byte *readbuf,
+ const gdb_byte *writebuf)
{
- //#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_return_value called");
-#endif
- //#else
- /* This will change with the ABI */
- int struct_return = (TYPE_CODE (valtype) == TYPE_CODE_STRUCT ||
- TYPE_CODE (valtype) == TYPE_CODE_UNION ||
- TYPE_LENGTH (valtype) > 8);
-
-
- if (writebuf != NULL)
- {
- gdb_assert (!struct_return);
- arc_store_return_value (valtype, regcache, writebuf);
- }
+ ENTERARGS("readbuf = %p, writebuf = %p", readbuf, writebuf);
- if (readbuf != NULL)
{
- gdb_assert (!struct_return);
- arc_extract_return_value (valtype, regcache, readbuf);
+ /* If the return type is a struct, or a union, or would occupy more
+ than two registers, the ABI uses the "struct return convention": the
+ calling function passes a hidden first parameter to the callee (in R0).
+ That parameter is the address at which the value being returned
+ should be stored. Otherwise, the result is returned in registers. */
+ Boolean is_struct_return = (TYPE_CODE (valtype) == TYPE_CODE_STRUCT ||
+ TYPE_CODE (valtype) == TYPE_CODE_UNION ||
+ TYPE_LENGTH (valtype) > 2 * BYTES_IN_REGISTER);
+
+ /* case a) */
+ if (writebuf != NULL)
+ {
+ /* gdb should not ask us to set a struct return value: it should
+ know the struct return location and write the value there
+ itself. */
+ gdb_assert (!is_struct_return);
+ store_return_value (valtype, regcache, writebuf);
+ }
+
+ /* case b) */
+ if (readbuf != NULL)
+ {
+ /* gdb should not ask us to get a struct return value: it should
+ know the struct return location and read the value from there
+ itself. */
+ gdb_assert (!is_struct_return);
+ extract_return_value (valtype, regcache, readbuf);
+ }
+
+ return (is_struct_return) ? RETURN_VALUE_STRUCT_CONVENTION
+ : RETURN_VALUE_REGISTER_CONVENTION;
}
+}
+
+
+/* Assuming that next_frame->prev is a dummy, return the frame ID of that dummy
+ frame. The frame ID's base needs to match the TOS value saved by
+ save_dummy_frame_tos() (!!!! WHAT IS THIS???), and the PC to match the dummy
+ frame's breakpoint. */
- if (struct_return)
- return RETURN_VALUE_STRUCT_CONVENTION;
- else
- return RETURN_VALUE_REGISTER_CONVENTION;
- //#endif
+static struct frame_id
+arc_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ return frame_id_build (arc_unwind_sp (gdbarch, next_frame),
+ frame_pc_unwind (next_frame));
}
-/* Signal Trampoline Frame Unwinder. These
- * unwinders allow frame unwinding to happen
- * from within signal handlers.
- */
-static struct arc_unwind_cache *
+/* Signal Trampoline Frame Unwinder.
+ These unwinders allow frame unwinding to happen from within signal handlers. */
+
+static UnwindCache *
arc_sigtramp_frame_cache (struct frame_info *next_frame,
- void **this_cache)
+ void **this_cache)
{
- // FIXMEA: cleanup#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_sigtramp_frame_cache called");
-#endif
- //#else
- struct arc_unwind_cache *cache;
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
- CORE_ADDR addr;
- char buf[4];
-
- if (*this_cache)
- return *this_cache;
+ ENTERMSG;
- cache = FRAME_OBSTACK_ZALLOC (struct arc_unwind_cache);
- (*this_cache) = cache;
- cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
-
- /* Zero all fields. */
- cache->blink_offset = 0;
- cache->prev_pc = 0;
- cache->prev_sp = 0;
- cache->frame_base = 0;
- cache->framesize = 0;
- cache->sp_offset = 0;
- cache->fp_offset = 0;
- cache->prev_pc = 0;
- cache->is_leaf = 0;
- cache->uses_fp = 0;
-
-
- frame_unwind_register (next_frame, SP_REGNUM, buf);
- cache->frame_base = extract_unsigned_integer (buf, 4);
-
- addr = tdep->sigcontext_addr (next_frame);
- if (tdep->sc_reg_offset)
+ if (*this_cache == NULL)
{
- int i;
-
- for (i = 0; i < tdep->sc_num_regs; i++)
- if (tdep->sc_reg_offset[i] != -1)
- cache->saved_regs[i].addr = addr + tdep->sc_reg_offset[i];
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ UnwindCache *cache = create_cache(next_frame);
+ gdb_byte buf[BYTES_IN_REGISTER];
+
+ *this_cache = cache;
+
+ /* Get the stack pointer and use it as the frame base. */
+ frame_unwind_register (next_frame, ARC_SP_REGNUM, buf);
+ cache->frame_base = (CORE_ADDR) extract_unsigned_integer (buf, BYTES_IN_REGISTER);
+
+ /* If the ARC-private target-dependent info has a table of offsets of
+ saved register contents within a O/S signal context structure. */
+ if (tdep->sc_reg_offset)
+ {
+ /* Find the address of the sigcontext structure. */
+ CORE_ADDR addr = tdep->sigcontext_addr (next_frame);
+ unsigned int i;
+
+ /* For each register, if its contents have been saved within the
+ sigcontext structure, determine the address of those contents. */
+ for (i = 0; i < tdep->sc_num_regs; i++)
+ if (tdep->sc_reg_offset[i] != REGISTER_NOT_PRESENT)
+ cache->saved_regs[i].addr = (LONGEST) (addr + tdep->sc_reg_offset[i]);
+ }
}
- return cache;
- //#endif
+ return *this_cache;
}
+
+/* Construct id for the given frame. */
+
static void
-arc_sigtramp_frame_this_id (struct frame_info *next_frame, void **this_cache,
- struct frame_id *this_id)
+arc_sigtramp_frame_this_id (struct frame_info *next_frame,
+ void **this_cache,
+ struct frame_id *this_id)
{
- //FIXMEA: cleanup #ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_sigtramp_frame_this_id called");
-#endif
- //#else
- struct arc_unwind_cache *cache =
- arc_sigtramp_frame_cache (next_frame, this_cache);
-
- (*this_id) = frame_id_build (cache->frame_base, frame_pc_unwind (next_frame));
- //#endif
+ UnwindCache *cache;
+
+ ENTERMSG;
+
+ cache = arc_sigtramp_frame_cache (next_frame, this_cache);
+
+ *this_id = frame_id_build (cache->frame_base, frame_pc_unwind (next_frame));
}
+
+/* Retrieve the value of the register in the frame. */
+
static void
arc_sigtramp_frame_prev_register (struct frame_info *next_frame,
- void **this_cache,
- int regnum, int *optimizedp,
- enum lval_type *lvalp, CORE_ADDR *addrp,
- int *realnump, void *valuep)
+ void **this_cache,
+ int regnum,
+ int *optimizedp,
+ enum lval_type *lvalp,
+ CORE_ADDR *addrp,
+ int *realnump,
+ gdb_byte *valuep)
{
- // FIXMEA: cleanup#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_sigtramp_frame_prev_register called");
-#endif
- //#else
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
-
- /* Make sure we've initialized the cache. */
- struct arc_unwind_cache *cache =
- arc_sigtramp_frame_cache (next_frame, this_cache);
+ ENTERMSG;
- /* on a signal, the PC is in ret */
-#ifdef ARC4_JTAG
- if (regnum == ARC_STATUS_REGNUM)
-#else
- if(regnum == PC_REGNUM)
-#endif
- regnum = tdep->pc_regnum_in_sigcontext;
-
- trad_frame_get_prev_register (next_frame, cache->saved_regs, regnum,
- optimizedp, lvalp, addrp, realnump, valuep);
- //#endif
+ {
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+
+ /* Make sure we've initialized the cache. */
+ UnwindCache *cache = arc_sigtramp_frame_cache (next_frame, this_cache);
+
+ /* On a signal, the PC is in ret. */
+ if (regnum == ARC_PC_REGNUM)
+ regnum = tdep->pc_regnum_in_sigcontext;
+
+ trad_frame_get_prev_register (next_frame,
+ cache->saved_regs,
+ regnum,
+ optimizedp,
+ lvalp,
+ addrp,
+ realnump,
+ valuep);
+ }
}
-static const struct frame_unwind arc_sigtramp_frame_unwind =
-{
- SIGTRAMP_FRAME,
- arc_sigtramp_frame_this_id,
- arc_sigtramp_frame_prev_register
-};
-
-const struct frame_unwind *
+static const struct frame_unwind *
arc_sigtramp_frame_sniffer (struct frame_info *next_frame)
{
- //FIXMEA: cleanup#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_sigtramp_frame_sniffer called() ");
-#endif
- //#else
- CORE_ADDR pc = frame_pc_unwind (next_frame);
- struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (next_frame));
+ ENTERMSG;
- /* We shouldn't even bother if we don't have a sigcontext_addr
- handler. */
- if (tdep->sigcontext_addr == NULL)
- return NULL;
-
- if (tdep->sigtramp_p != NULL)
{
- if (tdep->sigtramp_p (next_frame))
- {
- return &arc_sigtramp_frame_unwind;
- }
- }
+ struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (next_frame));
- return NULL;
- //#endif
-}
+ /* We don't even bother if we don't have a sigcontext_addr handler. */
+ if ((tdep->sigcontext_addr != NULL) &&
+ (tdep->is_sigtramp != NULL) &&
+ tdep->is_sigtramp (next_frame))
+ {
+ static const struct frame_unwind arc_sigtramp_frame_unwind =
+ {
+ SIGTRAMP_FRAME, // type
+ arc_sigtramp_frame_this_id, // this_id
+ arc_sigtramp_frame_prev_register, // prev_register
+ NULL, // unwind_data
+ NULL, // sniffer
+ NULL, // prev_pc
+ NULL // dealloc_cache
+ };
+
+ return &arc_sigtramp_frame_unwind;
+ }
+ return NULL;
+ }
+}
-/* Allow calls to be made to functions defined in the debuggee.
- a.k.a dummy calls
-*/
-/* When arguments must be pushed onto the stack, they go on in reverse
- order. The below implements a FILO (stack) to do this.
- Copied from d10v-tdep.c. */
+/* Allow calls to be made to functions defined in the debuggee (A.K.A dummy calls).
-struct stack_item
-{
- int len;
- struct stack_item *prev;
- void *data;
-};
+ Parameters:
+ gdbarch : gdbarch structure for the backend to use if needed.
+ function : the function to be called
+ regcache : the register cache to be used for altered register values
+ bp_addr : return address for the breakpoint.
+ nargs : the number of arguments to the function
+ args : the arguments to be passed to the function
+ sp : current value of SP.
+ struct_return: 1 if structures are returned by the function.
+ struct_addr : hidden address for returning a struct. */
-static struct stack_item *
-push_stack_item (struct stack_item *prev, void *contents, int len)
-{
- struct stack_item *si;
- si = xmalloc (sizeof (struct stack_item));
- si->data = xmalloc (len);
- si->len = len;
- si->prev = prev;
- memcpy (si->data, contents, len);
- return si;
-}
+static CORE_ADDR
+arc_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)
-static struct stack_item *
-pop_stack_item (struct stack_item *si)
{
- struct stack_item *dead = si;
- si = si->prev;
- xfree (dead->data);
- xfree (dead);
- return si;
-}
+ int arg_reg = ARC_ABI_FIRST_ARGUMENT_REGISTER;
+ ENTERARGS("nargs = %d, struct_return = %d", nargs, struct_return);
+ /* Push the return address. */
+ regcache_cooked_write_unsigned (regcache, ARC_BLINK_REGNUM, bp_addr);
+ DEBUG("set BLINK = %X\n", (unsigned int) bp_addr);
-/* arc_push_dummy_call :
- * gdbarch : gdbarch structure for the backend to use if needed.
- * function :
- * regcache :
- * bp_addr : Return address for the breakpoint.
- * sp : Current value of sp.
- * struct_return: struct_return is 1 if structures are returned by
- * the function.
- * struct_addr: Hidden address for returning a struct.
- */
+ /* Are we returning a value using a structure return instead of a normal
+ value return? If so, struct_addr is the address of the reserved space
+ for the return structure to be written on the stack, and that address
+ is passed to that function as a hidden first argument. */
+ if (struct_return)
+ {
+ /* Pass the return address in the first argument register. */
+ regcache_cooked_write_unsigned (regcache, arg_reg, struct_addr);
-static CORE_ADDR
-arc_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)
+ DEBUG("struct return address 0x%08lX passed in R%d", struct_addr, arg_reg);
-{
- //#ifdef ARC4_JTAG
-#ifdef ARC_DEBUG
- printf ("\narc_push_dummy_call called");
-#endif
- // #else
- int stack_alloc;
- int stack_offset;
- int argreg;
- int argnum;
-
- CORE_ADDR regval;
- struct stack_item *si = NULL;
-
-
- /* Push the return address. */
-#ifdef ARC4_JTAG
- CORE_ADDR modified_bp_addr;
- modified_bp_addr = arc_debug_fetch_regs(ARC_STATUS_REGNUM);
- regcache_raw_collect(regcache, ARC_STATUS_REGNUM, &modified_bp_addr);
- modified_bp_addr = modified_bp_addr & 0xff000000;
- bp_addr = bp_addr >>2;
- modified_bp_addr |= bp_addr;
- regcache_cooked_write_unsigned (regcache, ARC_BLINK_REGNUM, modified_bp_addr);
-#else
- regcache_cooked_write_unsigned (regcache, ARC_BLINK_REGNUM, bp_addr);
-#endif
+ arg_reg++;
+ }
- /* Are we returning a value using a structure return or a normal value
- return? struct_addr is the address of the reserved space for the return
- structure to be written on the stack.
- */
- /* FIXME:: Ramana :: What about 4 byte structures returned in r0 as
- claimed by Metaware.
- */
-
- /* Now load as many as possible of the first arguments into registers,
- and push the rest onto the stack. */
- argreg = ARC_ARG0_REGNUM;
-
- if (struct_return)
+ /* If the function has arguments. */
+ if (nargs > 0)
{
- regcache_cooked_write_unsigned (regcache, ARC_ARG0_REGNUM, struct_addr);
- argreg++;
-#ifdef ARC4_JTAG
- sp = sp - 16;
-#endif
- }
+ Boolean big_endian = gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG;
+ unsigned int total_space = 0;
+ gdb_byte *memory_image;
+ gdb_byte *data;
+ int i;
+
+ /* How much space do the arguments occupy in total?
+ N.B. must round each argument's size up to an integral number of words. */
+ for (i = 0; i < nargs; i++)
+ {
+ unsigned int len = TYPE_LENGTH (value_type (args[i]));
+ unsigned int space = ROUND_UP_TO_WORDS(len);
- stack_offset = 0;
+ total_space += space;
- for (argnum = 0; argnum < nargs; argnum++)
- {
- int len;
- char *val;
- int reg_demand;
- int i;
-
- len = TYPE_LENGTH (VALUE_TYPE (args[argnum]));
- val = (char *) VALUE_CONTENTS (args[argnum]);
-
- /* How may registers worth of storage do we need for this argument? */
- reg_demand = (len / 4) + (len % 4 != 0 ? 1 : 0);
-
- if (argreg + reg_demand - 1 <= ARC_ARG7_REGNUM)
+ DEBUG("function arg[%d]: %d bytes -> %d\n", i, len, space);
+ }
+
+ /* Allocate a buffer to hold a memory image of the arguments. */
+ memory_image = XCALLOC((size_t) total_space, gdb_byte);
+ if (memory_image == NULL)
{
- /* Data passed by value. Fits in available register(s). */
- for (i = 0; i < reg_demand; i++)
- {
- regcache_cooked_write_unsigned (regcache, argreg,
- *(unsigned long *) val);
- argreg++;
- val += 4;
- }
+ /* could not do the call! */
+ return 0;
}
- else if (argreg <= ARC_ARG7_REGNUM)
+
+ /* Now copy all of the arguments into the buffer, correctly aligned. */
+ data = memory_image;
+ for (i = 0; i < nargs; i++)
{
- /* Data passed by value. Does not fit in available register(s).
- Use the register(s) first, then the stack. */
- for (i = 0; i < reg_demand; i++)
- {
- if (argreg <= ARC_ARG7_REGNUM)
- {
- regcache_cooked_write_unsigned (regcache, argreg,
- *(unsigned long *) val);
- argreg++;
- val += 4;
- }
- else
- {
- /* Push item for later so that pushed arguments
- come in the right order. */
- si = push_stack_item (si, val, 4);
- val += 4;
- }
- }
+ unsigned int len = TYPE_LENGTH (value_type (args[i]));
+ unsigned int space = ROUND_UP_TO_WORDS(len);
+ gdb_byte* actual = data;
+ unsigned int w;
+
+ /* Parameters smaller than a word are normally aligned by gcc to the
+ least significant byte, so if we have a big-endian target we put
+ the data at the end of the word rather than at the start. */
+ if ((len < BYTES_IN_WORD) && big_endian)
+ actual += BYTES_IN_WORD - len;
+
+ (void) memcpy(actual, value_contents (args[i]), (size_t) len);
+
+ DEBUG("function arg[%d] =", i);
+ for (w = 0; w < space / BYTES_IN_WORD; w++)
+ DEBUG(" 0x%08X", *((int*) data + w));
+ DEBUG("\n");
+
+ data += space;
}
- else if (len > (2 * 4))
+
+ /* Now load as much as possible of the memory image into registers. */
+ data = memory_image;
+ while (arg_reg <= ARC_ABI_LAST_ARGUMENT_REGISTER)
{
- /* FIXME */
- internal_error (__FILE__, __LINE__, "We don't do this");
+ ARC_RegisterContents contents = *(ARC_RegisterContents*) data;
+
+ DEBUG("passing 0x%08X in register R%d\n", (unsigned int) contents, arg_reg);
+
+ /* Convert to target byte order if necessary. */
+ if (HOST_AND_TARGET_ENDIANNESS_DIFFER(gdbarch))
+ {
+ contents = __bswap_32(contents);
+ DEBUG("byte-swapped to 0x%08X\n", contents);
+ }
+
+ regcache_cooked_write_unsigned (regcache,
+ arg_reg,
+ (ULONGEST) contents);
+
+ data += BYTES_IN_REGISTER;
+ total_space -= BYTES_IN_REGISTER;
+
+ /* If all the data is now in registers. */
+ if (total_space == 0)
+ break;
+
+ arg_reg++;
}
- else
+
+ /* If there is any data left, push it onto the stack (in a single write operation). */
+ if (total_space > 0)
{
- /* Data passed by value. No available registers. Put it on
- the stack. */
- si = push_stack_item (si, val, len);
+ DEBUG("passing %d bytes on stack\n", total_space);
+
+ sp -= total_space;
+ write_memory (sp, data, (int) total_space);
}
- }
- while (si)
- {
- /* fp_arg must be word-aligned (i.e., don't += len) to match
- the function prologue. */
- sp = (sp - si->len) & ~3;
-#ifdef ARC4_JTAG
- write_memory (sp + 16, si->data, si->len);
-#else
- write_memory (sp, si->data, si->len);
-#endif
- si = pop_stack_item (si);
+ xfree(memory_image);
}
- /* Finally, update the SP register. */
- regcache_cooked_write_unsigned (regcache, ARC_SP_REGNUM, sp);
+ /* Finally, update the SP register. */
+ regcache_cooked_write_unsigned (regcache, ARC_SP_REGNUM, sp);
+
+ DEBUG("set SP = %X\n", (unsigned int) sp);
- return sp;
- //#endif
+ return sp;
}
-/* Align Frame */
+
+/* Align frame. */
+
static CORE_ADDR
arc_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
{
- /* Align to the normal alignment on the stack). */
- return sp & ~3;
+ /* Align to the normal alignment on the stack). */
+ return WORD_ALIGNED(sp);
}
/* Print interesting information about the floating point processor
(if present) or emulator. */
+
static void
arc_print_float_info (struct gdbarch *gdbarch, struct ui_file *file,
- struct frame_info *frame, const char *args)
+ struct frame_info *frame, const char *args)
{
- printf("Software FPU \n");
+ printf(_("Software FPU\n"));
}
/* Set the main_name to "_main" if required.
This is set as an observer of inferior_created. */
+
static void
arc_set_main_name (struct target_ops *objfile, int from_tty)
{
- struct minimal_symbol *umainsym, *mainsym;
-
- /* Old ARC toolchains prepend an underscore to symbol names. If there is
- an _main but no main, then we're probably debugging a binary that was
- made with the old toolchain. */
- umainsym = lookup_minimal_symbol ("_main", NULL, NULL);
- mainsym = lookup_minimal_symbol ("main", NULL, NULL);
- if(umainsym && !mainsym)
+ /* Old ARC toolchains prepend an underscore to symbol names. If there is
+ an _main but no main, then we're probably debugging a binary that was
+ made with the old toolchain. */
+ struct minimal_symbol *umainsym = lookup_minimal_symbol ("_main", NULL, NULL);
+ struct minimal_symbol *mainsym = lookup_minimal_symbol ("main", NULL, NULL);
+
+ if ((umainsym != NULL) && (mainsym == NULL))
{
- set_main_name ("_main");
+ set_main_name ("_main");
}
- /* If we don't have any symbols, the default, i.e. "main", will get used. */
+ /* If we don't have any symbols, the default, i.e. "main", will get used. */
}
-/* The following piece of code is borrowed from d10v */
-static void
-a4_address_to_pointer (struct type *type, void *buf, CORE_ADDR addr)
+/* This initialization function is called by gdb.
+ See gdbarch.h for a description of its parameters. */
+
+static struct gdbarch*
+arc_gdbarch_init (struct gdbarch_info info,
+ struct gdbarch_list *arches)
{
-#ifdef ARC4_JTAG
- if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_FUNC
- || TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD)
- store_unsigned_integer (buf, TYPE_LENGTH (type), (addr>>2) & 0xffffff);
- else
- store_unsigned_integer (buf, TYPE_LENGTH (type), addr);
+ /* Allocate the ARC-private target-dependent information structure, and the
+ gdb target-independent information structure. */
+ struct gdbarch_tdep *tdep = xmalloc (sizeof (struct gdbarch_tdep));
+ struct gdbarch *gdbarch = gdbarch_alloc (&info, tdep);
+
+ memset(tdep, 0, sizeof(*tdep));
+
+ /* Put stuff in gdbarch. */
+
+ /* Breakpoint manipulation. */
+ set_gdbarch_breakpoint_from_pc (gdbarch, arc_breakpoint_from_pc);
+ set_gdbarch_decr_pc_after_break (gdbarch, 0);
+
+ info.osabi = CONFIG_OSABI;
+ gdbarch_init_osabi(info, gdbarch);
+
+ /* Characters are unsigned by default. */
+ set_gdbarch_char_signed (gdbarch, 0);
+
+ set_gdbarch_sp_regnum (gdbarch, ARC_SP_REGNUM);
+ set_gdbarch_register_type (gdbarch, arc_register_type);
+ set_gdbarch_print_float_info (gdbarch, arc_print_float_info);
+
+ /* Advance PC across function entry code. */
+ set_gdbarch_skip_prologue (gdbarch, arc_skip_prologue);
+
+ /* Hook in the Dwarf-2 frame sniffer. */
+ dwarf2_frame_set_init_reg (gdbarch, arc_dwarf2_frame_init_reg);
+ frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
+
+ /* Signal frames. */
+ frame_unwind_append_sniffer (gdbarch, arc_sigtramp_frame_sniffer);
+
+ /* The stack grows downward. */
+ set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+ set_gdbarch_unwind_pc (gdbarch, arc_unwind_pc);
+ set_gdbarch_unwind_sp (gdbarch, arc_unwind_sp);
+ set_gdbarch_unwind_dummy_id (gdbarch, arc_unwind_dummy_id);
+ frame_unwind_append_sniffer (gdbarch, arc_frame_sniffer);
+ set_gdbarch_return_value (gdbarch, arc_return_value);
+
+ /* Add the ARC register groups. */
+ reggroup_add (gdbarch, general_reggroup);
+ reggroup_add (gdbarch, all_reggroup);
+ reggroup_add (gdbarch, system_reggroup);
+
+ set_gdbarch_register_reggroup_p (gdbarch, arc_register_reggroup_p);
+
+ set_gdbarch_cannot_step_breakpoint (gdbarch, 1);
+ set_gdbarch_frame_align(gdbarch, arc_frame_align);
+
+ /* Dummy Frame handling. */
+ set_gdbarch_push_dummy_call (gdbarch, arc_push_dummy_call);
+ set_gdbarch_call_dummy_location (gdbarch, AT_ENTRY_POINT);
+
+ /* N.B. we do not want to call arcompact_get_disassembler at this point, as we do
+ not as yet have an executable file, so do not know whether we want
+ the disassembler for the A4 architecture (still supported by the
+ opcodes library!) or the one for the ARCompact architecture; also,
+ it is better to pass the arcompact_get_disassembler function a valid bfd
+ structure, rather than a faked one, so that it can read the ARC-
+ specific sections in the ELF file (for whatever reason...).
+
+ So just set up a dummy disassembler at this point (gdb requires that
+ *some* disassembler is defined for an architecture), and set up a
+ callback which will set up the appropriate disassembler once an
+ executable file has been selected for debugging. */
+
+#if 0
+ {
+ /* The arc libopcodes wants abfd so that it can find out what CPU
+ extensions are there. */
+ bfd abfd;
+
+ abfd.sections = NULL;
+
+ set_gdbarch_print_insn(gdbarch, arcompact_get_disassembler(&abfd));
+ }
#endif
+ set_gdbarch_print_insn(gdbarch, dummy_disassembler);
+ (void) observer_attach_new_objfile(set_disassembler);
+
+ /* Set main_name to _main if necessary. Ideally we'd want a hook that
+ gets called when symbols are loaded, but it seems there isn't one; so
+ we'll use this. This will not work if the user does "target remote
+ ..." and then "add-symbol-file ...". */
+ (void) observer_attach_inferior_created (arc_set_main_name);
+
+ /* Initialize the target-dependent modules (if any). */
+ CONFIG_INIT_TDEP
+
+ /* Return a pointer to the new object - indicates that a new architecture
+ has been created. */
+ return gdbarch;
}
-static CORE_ADDR
-a4_pointer_to_address (struct type *type, const void *buf)
+
+static void
+arc_dump_tdep (struct gdbarch *current_gdbarch, struct ui_file *file)
{
-#ifdef ARC4_JTAG
- CORE_ADDR addr = extract_unsigned_integer (buf, TYPE_LENGTH(type));
- /* Is it a code address? */
- if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_FUNC
- || TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD
- || TYPE_CODE_SPACE (TYPE_TARGET_TYPE (type)))
- return ((addr<<2) & 0x2ffffff);
- else
- return addr;
-#endif
+ /* Do nothing. */
}
-static struct gdbarch *
-arc_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+
+/* -------------------------------------------------------------------------- */
+/* local functions implementing commands */
+/* -------------------------------------------------------------------------- */
+
+/* <command> [ <frame> ]
+ Display annotated stack frame. */
+
+static void
+arc_show_stack_frame_command (char *arg, int from_tty)
{
- struct gdbarch_tdep *tdep;
- struct gdbarch *gdbarch;
+ struct frame_info *fi;
+ gdb_byte value[MAX_REGISTER_SIZE];
+ CORE_ADDR pc, fp, sp, old_sp;
+ CORE_ADDR addr;
+ enum lval_type lval;
+ int optimized;
+ int realnum;
+ int frame_size;
+ unsigned int *frame_contents;
+ LocalVariable *variables;
+ unsigned int num_variables;
+
+ /* Find the frame. */
+ fi = parse_frame_specification_1 (arg, _("No stack."), NULL);
+ gdb_assert(fi != NULL);
+
+ /* Find out PC, FP and SP for the frame. */
+ pc = get_frame_pc(fi);
+ sp = get_frame_sp(fi);
+ get_frame_register(fi, ARC_FP_REGNUM, value);
+ fp = (CORE_ADDR) extract_unsigned_integer (value, BYTES_IN_REGISTER);
+
+ DEBUG("*** PC = %x, FP = %x, SP = %x\n", (unsigned int) pc, (unsigned int) fp, (unsigned int) sp);
+
+ if (pc == 0)
+ {
+ warning(_("invalid frame"));
+ return;
+ }
- tdep = xmalloc (sizeof (struct gdbarch_tdep));
- gdbarch = gdbarch_alloc (&info, tdep);
-
- /* Fixme :: Worry about default initialization of breakpoints
- for the ARC platform. In our case currently this is handled
- out of arc-linux-tdep.c for default arc linux breakpoints.
- */
+ frame_register_unwind (fi, ARC_SP_REGNUM, &optimized, &lval, &addr, &realnum, value);
+ old_sp = (CORE_ADDR) extract_unsigned_integer (value, BYTES_IN_REGISTER);
- info.osabi = CONFIG_OSABI;
- gdbarch_init_osabi(info, gdbarch);
-
- /* Put stuff in gdbarch. */
+ /* Find the local variables and parameters. */
+ variables = find_local_variables(fi, &num_variables);
- /* Characters are unsigned by default */
- set_gdbarch_char_signed (gdbarch, 0);
+ printf_filtered(NEW_LINE);
+ identify_frame(fi);
- set_gdbarch_print_float_info (gdbarch, arc_print_float_info);
- set_gdbarch_sp_regnum (gdbarch, ARC_SP_REGNUM);
- set_gdbarch_register_type (gdbarch, arc_register_type);
+ DEBUG(_("\n\n*** FRAME: 0x%x .. 0x%x\n\n"), (unsigned int) sp, (unsigned int) (old_sp - 1));
- set_gdbarch_cannot_store_register (gdbarch, arc_cannot_store_register);
+ frame_size = (int) (old_sp - sp);
+ frame_contents = xmalloc((size_t) frame_size);
- /* Advance PC across function entry code. */
- set_gdbarch_skip_prologue (gdbarch, arc_skip_prologue);
+ if (frame_contents)
+ {
+ /* Read all of the frame's contents from target memory. */
+ if (target_read_memory(sp, (gdb_byte*) frame_contents, frame_size) == 0)
+ {
+ int numregs = ARC_TOTAL_REGS;
+ int i = frame_size / BYTES_IN_WORD - 1;
+ unsigned int the_same = 0;
+ unsigned int previous_word = 0;
+ Boolean first_word = TRUE;
+ Boolean is_following_element;
+ Boolean elements_are_word_sized;
-
- /* Hook in the Dwarf-2 frame sniffer. */
- set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arc_binutils_reg_to_regnum);
- dwarf2_frame_set_init_reg (gdbarch, arc_dwarf2_frame_init_reg);
- frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
+ addr = old_sp - BYTES_IN_WORD;
- /* signal frames */
- frame_unwind_append_sniffer (gdbarch, arc_sigtramp_frame_sniffer);
+ printf_filtered(NEW_LINE);
+ /* Traverse the frame from high address to low address, one word at a time. */
+ while (i >= 0)
+ {
+ unsigned int this_word = frame_contents[i];
+ Boolean print = first_word; /* Always print the first word. */
+ char line[256];
+ int n;
+ /* Start with a blank line. */
+ (void) memset(line, (int) ' ', sizeof(line));
- /* The stack grows downward. */
- set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+ /* Does FP and/or SP address this particular stack location? */
+ if (addr == sp && fp == sp)
+ {
+ (void) strcpy(line + 14, _("SP/FP ===> "));
+ line[25] = ' ';
+ print = TRUE;
+ }
+ else if (addr == sp)
+ {
+ (void) strcpy(line + 17, _("SP ===> "));
+ line[25] = ' ';
+ print = TRUE;
+ }
+ else if (addr == fp)
+ {
+ (void) strcpy(line + 17, _("FP ===> "));
+ line[25] = ' ';
+ print = TRUE;
+ }
- set_gdbarch_unwind_pc (gdbarch, arc_unwind_pc);
- set_gdbarch_unwind_sp (gdbarch, arc_unwind_sp);
- set_gdbarch_unwind_dummy_id (gdbarch, arc_unwind_dummy_id);
- frame_unwind_append_sniffer (gdbarch, arc_frame_sniffer);
-
+ /* Does this stack location hold a local variable or parameter? */
+ if ((add_local_name(line + 38,
+ addr,
+ variables,
+ num_variables,
+ &is_following_element,
+ &elements_are_word_sized)))
+ {
+ /* Yes - so we want to print it (but this may be revised below!). */
+ print = TRUE;
+ }
+ else
+ {
+ int r;
+
+ /* Does this location hold a saved register? */
+ for (r = 0; r < numregs; r++)
+ {
+ if (r != ARC_SP_REGNUM &&
+ gdbarch_register_reggroup_p (current_gdbarch, r, all_reggroup))
+ {
+ CORE_ADDR saved_addr;
+
+ /* Find out the location of the saved register without
+ fetching the corresponding value. */
+ frame_register_unwind (fi, r, &optimized, &lval, &saved_addr, &realnum, NULL);
+
+ if (!optimized && lval == lval_memory && saved_addr == addr)
+ {
+ (void) sprintf(line + 38, _("saved register '%s'"), gdbarch_register_name(current_gdbarch, r));
+ print = TRUE;
+ break;
+ }
+ }
+ }
+ }
- set_gdbarch_return_value (gdbarch, arc_return_value);
+ /* Do we think we need to print out the line? */
+ if (print)
+ {
+ /* If the location is a non-zeroth word-sized array element,
+ and this word is the same as the previous word. */
+ if (is_following_element && elements_are_word_sized && (this_word == previous_word))
+ {
+ /* No need to print it - one more word the same! */
+ the_same++;
+ print = FALSE;
+ }
+ }
+ else
+ {
+ /* N.B. this will not be the first word (since print is FALSE) */
+
+ /* This location does not hold anything "interesting", but
+ if this word is not the same as the previous word, we
+ want to print it. */
+ if (this_word == previous_word)
+ the_same++;
+ else
+ print = TRUE;
+ }
- /* Add the arc register groups. */
- arc_add_reggroups (gdbarch);
- set_gdbarch_register_reggroup_p (gdbarch, arc_register_reggroup_p);
-
- /* Breakpoint manipulation. */
- set_gdbarch_breakpoint_from_pc (gdbarch, arc_breakpoint_from_pc);
- set_gdbarch_frame_align(gdbarch,arc_frame_align);
+ /* OK, now we really know whether we need to print out the line. */
+ if (print)
+ {
+ n = sprintf(line, _("0x%08X:"), (unsigned int) addr);
+ line[n] = ' ';
+
+ n = sprintf(line + 25, "%08X", this_word);
+ line[25 + n] = ' ';
+
+ n = (int) sizeof(line) - 1;
+ while (line[n] == ' ') n--;
+ line[n + 1] = '\0';
+
+ /* If we did not print the previous word because it was the
+ same as the word before, but not the same as any preceding
+ words, print it now (there is no point in outputting a
+ message which says simply "... 1 words omitted" - we might
+ just as well print out the word! */
+ if (the_same == 1)
+ printf_filtered(_("0x%08X: %08X\n"), (unsigned int) addr + BYTES_IN_WORD, previous_word);
+ else if (the_same != 0)
+ /* 2 or more words the same... */
+ printf_filtered(_("... %u words omitted\n"), the_same);
+
+ /* Now print out the line with the current word (and other information). */
+ printf_filtered(_("%s\n"), line);
+
+ /* No words omitted. */
+ the_same = 0;
+ }
- /* Dummy Frame handling */
- set_gdbarch_push_dummy_call (gdbarch, arc_push_dummy_call);
- set_gdbarch_call_dummy_location (gdbarch,AT_ENTRY_POINT);
-
- /* Disassembly. */
- {
- /* the arc libopcodes wants abfd so that it can find out what CPU
- extensions are there */
- bfd abfd;
- abfd.sections = NULL;
+ first_word = FALSE;
+ previous_word = this_word;
+ addr -= BYTES_IN_WORD;
+ i--;
+ }
-#ifndef ARC4_JTAG
- set_gdbarch_print_insn(gdbarch, arcompact_get_disassembler(&abfd));
-#else
- set_gdbarch_print_insn(gdbarch, arc_get_disassembler(&abfd));
-#endif
- }
+ printf_filtered(NEW_LINE);
+ }
-#ifdef ARC4_JTAG
- set_gdbarch_address_to_pointer (gdbarch, a4_address_to_pointer);
- set_gdbarch_pointer_to_address (gdbarch, a4_pointer_to_address);
-#endif
- //#ifndef ARC4_JTAG
- /* Set main_name to _main if necessary. Ideally we'd want a hook that
- gets called when symbols are loaded, but it seems there isn't one; so
- we'll use this. This will not work if the user does "target remote
- ..." and then "add-symbol-file ..." */
- observer_attach_inferior_created (arc_set_main_name);
- //#endif
-
-#ifdef ARC4_JTAG
- // set_gdbarch_write_pc (gdbarch, a4_write_pc);
-#endif
+ xfree(frame_contents);
+ }
- CONFIG_INIT_TDEP (gdbarch);
-
- return gdbarch;
+ xfree(variables);
}
-static void
-arc_dump_tdep (struct gdbarch *current_gdbarch, struct ui_file *file)
+
+/* -------------------------------------------------------------------------- */
+/* externally visible functions */
+/* -------------------------------------------------------------------------- */
+
+/* Initialize the module. This function is called from the gdb core on start-up. */
+
+void
+_initialize_arc_tdep (void)
{
+ /* Register the ARC processor architecture with gdb, providing an
+ initialization function and a dump function; 'bfd_arch_arc' is
+ an enumeration value specifically denoting the ARC architecture. */
+ gdbarch_register(bfd_arch_arc, arc_gdbarch_init, arc_dump_tdep);
+
+ /* Register ARC-specific commands with gdb. */
+
+ add_setshow_boolean_cmd(DEBUG_COMMAND,
+ no_class,
+ &arc_debug_target,
+ _("Set whether to print ARC debug messages.\n"),
+ _("Show whether to print ARC debug messages.\n"),
+ _("If set debug messages are printed.\n"),
+ NULL,
+ NULL,
+ &setlist,
+ &showlist);
+
+ (void) add_cmd(SHOW_FRAME_COMMAND,
+ class_obscure,
+ arc_show_stack_frame_command,
+ _("Display the stack frame with annotation.\n"
+ SHOW_FRAME_COMMAND_USAGE
+ "<FRAME> may be the number or address of a frame.\n"),
+ &cmdlist);
}
+
+/* Initialize the info structure to enable the disassembler to be used. */
+
void
-_initialize_arc_tdep (void)
+arc_initialize_disassembler (struct disassemble_info *info)
{
- gdbarch_register (bfd_arch_arc, arc_gdbarch_init, arc_dump_tdep);
+ /* N.B. this type cast is not strictly correct: the return types differ! */
+ init_disassemble_info(info, gdb_stdout, (fprintf_ftype) fprintf_filtered);
+ info->arch = gdbarch_bfd_arch_info(current_gdbarch)->arch;
+ info->mach = gdbarch_bfd_arch_info(current_gdbarch)->mach;
+ info->endian = gdbarch_byte_order (current_gdbarch);
+ info->read_memory_func = read_memory_for_disassembler;
}
+/******************************************************************************/