/* Target-dependent code for GNU/Linux running on the Fujitsu FR-V,
for GDB.
- Copyright 2004 Free Software Foundation, Inc.
+
+ Copyright (C) 2004, 2006, 2007 Free Software Foundation, Inc.
This file is part of GDB.
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. */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
#include "defs.h"
+#include "gdbcore.h"
#include "target.h"
#include "frame.h"
#include "osabi.h"
+#include "regcache.h"
#include "elf-bfd.h"
#include "elf/frv.h"
#include "frv-tdep.h"
+#include "trad-frame.h"
+#include "frame-unwind.h"
+#include "regset.h"
+#include "gdb_string.h"
/* Define the size (in bytes) of an FR-V instruction. */
static const int frv_instr_size = 4;
return retval;
}
-/* Given NEXT_FRAME, "callee" frame of the sigtramp frame that we
+/* Given NEXT_FRAME, the "callee" frame of the sigtramp frame that we
wish to decode, and REGNO, one of the frv register numbers defined
in frv-tdep.h, return the address of the saved register (corresponding
to REGNO) in the sigtramp frame. Return -1 if the register is not
found in the sigtramp frame. The magic numbers in the code below
were computed by examining the following kernel structs:
- From arch/frvnommu/signal.c:
+ From arch/frv/kernel/signal.c:
struct sigframe
{
uint32_t retcode[2];
};
- From include/asm-frvnommu/ucontext.h:
+ From include/asm-frv/ucontext.h:
struct ucontext {
unsigned long uc_flags;
sigset_t uc_sigmask;
};
- From include/asm-frvnommu/sigcontext.h:
+ From include/asm-frv/signal.h:
+
+ typedef struct sigaltstack {
+ void *ss_sp;
+ int ss_flags;
+ size_t ss_size;
+ } stack_t;
+
+ From include/asm-frv/sigcontext.h:
struct sigcontext {
struct user_context sc_context;
unsigned long sc_oldmask;
} __attribute__((aligned(8)));
- From include/asm-frvnommu/registers.h:
+ From include/asm-frv/registers.h:
struct user_int_regs
{
unsigned long psr;
void *extension;
} __attribute__((aligned(8))); */
-static CORE_ADDR
+static LONGEST
frv_linux_sigcontext_reg_addr (struct frame_info *next_frame, int regno,
CORE_ADDR *sc_addr_cache_ptr)
{
else if (tramp_type == RT_SIGTRAMP)
{
/* For a realtime sigtramp frame, SP + 12 contains a pointer
- to the a ucontext struct. The ucontext struct contains
- a sigcontext struct starting 12 bytes in. */
+ to a ucontext struct. The ucontext struct contains a
+ sigcontext struct starting 24 bytes in. (The offset of
+ uc_mcontext within struct ucontext is derived as follows:
+ stack_t is a 12-byte struct and struct sigcontext is
+ 8-byte aligned. This gives an offset of 8 + 12 + 4 (for
+ padding) = 24.) */
if (target_read_memory (sp + 12, buf, sizeof buf) != 0)
{
- warning ("Can't read realtime sigtramp frame.");
+ warning (_("Can't read realtime sigtramp frame."));
return 0;
}
sc_addr = extract_unsigned_integer (buf, sizeof buf);
- sc_addr += 12;
+ sc_addr += 24;
}
else
- internal_error (__FILE__, __LINE__, "not a signal trampoline");
+ internal_error (__FILE__, __LINE__, _("not a signal trampoline"));
if (sc_addr_cache_ptr)
*sc_addr_cache_ptr = sc_addr;
}
}
+/* Signal trampolines. */
+
+static struct trad_frame_cache *
+frv_linux_sigtramp_frame_cache (struct frame_info *next_frame, void **this_cache)
+{
+ struct trad_frame_cache *cache;
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ CORE_ADDR addr;
+ char buf[4];
+ int regnum;
+ CORE_ADDR sc_addr_cache_val = 0;
+ struct frame_id this_id;
+
+ if (*this_cache)
+ return *this_cache;
+
+ cache = trad_frame_cache_zalloc (next_frame);
+
+ /* FIXME: cagney/2004-05-01: This is is long standing broken code.
+ The frame ID's code address should be the start-address of the
+ signal trampoline and not the current PC within that
+ trampoline. */
+ frame_unwind_register (next_frame, sp_regnum, buf);
+ this_id = frame_id_build (extract_unsigned_integer (buf, sizeof buf),
+ frame_pc_unwind (next_frame));
+ trad_frame_set_id (cache, this_id);
+
+ for (regnum = 0; regnum < frv_num_regs; regnum++)
+ {
+ LONGEST reg_addr = frv_linux_sigcontext_reg_addr (next_frame, regnum,
+ &sc_addr_cache_val);
+ if (reg_addr != -1)
+ trad_frame_set_reg_addr (cache, regnum, reg_addr);
+ }
+
+ *this_cache = cache;
+ return cache;
+}
+
+static void
+frv_linux_sigtramp_frame_this_id (struct frame_info *next_frame, void **this_cache,
+ struct frame_id *this_id)
+{
+ struct trad_frame_cache *cache =
+ frv_linux_sigtramp_frame_cache (next_frame, this_cache);
+ trad_frame_get_id (cache, this_id);
+}
+
+static void
+frv_linux_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, gdb_byte *valuep)
+{
+ /* Make sure we've initialized the cache. */
+ struct trad_frame_cache *cache =
+ frv_linux_sigtramp_frame_cache (next_frame, this_cache);
+ trad_frame_get_register (cache, next_frame, regnum, optimizedp, lvalp,
+ addrp, realnump, valuep);
+}
+
+static const struct frame_unwind frv_linux_sigtramp_frame_unwind =
+{
+ SIGTRAMP_FRAME,
+ frv_linux_sigtramp_frame_this_id,
+ frv_linux_sigtramp_frame_prev_register
+};
+
+static const struct frame_unwind *
+frv_linux_sigtramp_frame_sniffer (struct frame_info *next_frame)
+{
+ CORE_ADDR pc = frame_pc_unwind (next_frame);
+ char *name;
+
+ find_pc_partial_function (pc, &name, NULL, NULL);
+ if (frv_linux_pc_in_sigtramp (pc, name))
+ return &frv_linux_sigtramp_frame_unwind;
+
+ return NULL;
+}
+
+\f
+/* The FRV kernel defines ELF_NGREG as 46. We add 2 in order to include
+ the loadmap addresses in the register set. (See below for more info.) */
+#define FRV_ELF_NGREG (46 + 2)
+typedef unsigned char frv_elf_greg_t[4];
+typedef struct { frv_elf_greg_t reg[FRV_ELF_NGREG]; } frv_elf_gregset_t;
+
+typedef unsigned char frv_elf_fpreg_t[4];
+typedef struct
+{
+ frv_elf_fpreg_t fr[64];
+ frv_elf_fpreg_t fner[2];
+ frv_elf_fpreg_t msr[2];
+ frv_elf_fpreg_t acc[8];
+ unsigned char accg[8];
+ frv_elf_fpreg_t fsr[1];
+} frv_elf_fpregset_t;
+
+/* Constants for accessing elements of frv_elf_gregset_t. */
+
+#define FRV_PT_PSR 0
+#define FRV_PT_ISR 1
+#define FRV_PT_CCR 2
+#define FRV_PT_CCCR 3
+#define FRV_PT_LR 4
+#define FRV_PT_LCR 5
+#define FRV_PT_PC 6
+#define FRV_PT_GNER0 10
+#define FRV_PT_GNER1 11
+#define FRV_PT_IACC0H 12
+#define FRV_PT_IACC0L 13
+
+/* Note: Only 32 of the GRs will be found in the corefile. */
+#define FRV_PT_GR(j) ( 14 + (j)) /* GRj for 0<=j<=63. */
+
+#define FRV_PT_TBR FRV_PT_GR(0) /* gr0 is always 0, so TBR is stuffed
+ there. */
+
+/* Technically, the loadmap addresses are not part of `pr_reg' as
+ found in the elf_prstatus struct. The fields which communicate the
+ loadmap address appear (by design) immediately after `pr_reg'
+ though, and the BFD function elf32_frv_grok_prstatus() has been
+ implemented to include these fields in the register section that it
+ extracts from the core file. So, for our purposes, they may be
+ viewed as registers. */
+
+#define FRV_PT_EXEC_FDPIC_LOADMAP 46
+#define FRV_PT_INTERP_FDPIC_LOADMAP 47
+
+
+/* Unpack an frv_elf_gregset_t into GDB's register cache. */
+
+static void
+frv_linux_supply_gregset (const struct regset *regset,
+ struct regcache *regcache,
+ int regnum, const void *gregs, size_t len)
+{
+ int regi;
+ char zerobuf[MAX_REGISTER_SIZE];
+ const frv_elf_gregset_t *gregsetp = gregs;
+
+ memset (zerobuf, 0, MAX_REGISTER_SIZE);
+
+ /* gr0 always contains 0. Also, the kernel passes the TBR value in
+ this slot. */
+ regcache_raw_supply (regcache, first_gpr_regnum, zerobuf);
+
+ for (regi = first_gpr_regnum + 1; regi <= last_gpr_regnum; regi++)
+ {
+ if (regi >= first_gpr_regnum + 32)
+ regcache_raw_supply (regcache, regi, zerobuf);
+ else
+ regcache_raw_supply (regcache, regi,
+ gregsetp->reg[FRV_PT_GR (regi - first_gpr_regnum)]);
+ }
+
+ regcache_raw_supply (regcache, pc_regnum, gregsetp->reg[FRV_PT_PC]);
+ regcache_raw_supply (regcache, psr_regnum, gregsetp->reg[FRV_PT_PSR]);
+ regcache_raw_supply (regcache, ccr_regnum, gregsetp->reg[FRV_PT_CCR]);
+ regcache_raw_supply (regcache, cccr_regnum, gregsetp->reg[FRV_PT_CCCR]);
+ regcache_raw_supply (regcache, lr_regnum, gregsetp->reg[FRV_PT_LR]);
+ regcache_raw_supply (regcache, lcr_regnum, gregsetp->reg[FRV_PT_LCR]);
+ regcache_raw_supply (regcache, gner0_regnum, gregsetp->reg[FRV_PT_GNER0]);
+ regcache_raw_supply (regcache, gner1_regnum, gregsetp->reg[FRV_PT_GNER1]);
+ regcache_raw_supply (regcache, tbr_regnum, gregsetp->reg[FRV_PT_TBR]);
+ regcache_raw_supply (regcache, fdpic_loadmap_exec_regnum,
+ gregsetp->reg[FRV_PT_EXEC_FDPIC_LOADMAP]);
+ regcache_raw_supply (regcache, fdpic_loadmap_interp_regnum,
+ gregsetp->reg[FRV_PT_INTERP_FDPIC_LOADMAP]);
+}
+
+/* Unpack an frv_elf_fpregset_t into GDB's register cache. */
+
+static void
+frv_linux_supply_fpregset (const struct regset *regset,
+ struct regcache *regcache,
+ int regnum, const void *gregs, size_t len)
+{
+ int regi;
+ const frv_elf_fpregset_t *fpregsetp = gregs;
+
+ for (regi = first_fpr_regnum; regi <= last_fpr_regnum; regi++)
+ regcache_raw_supply (regcache, regi, fpregsetp->fr[regi - first_fpr_regnum]);
+
+ regcache_raw_supply (regcache, fner0_regnum, fpregsetp->fner[0]);
+ regcache_raw_supply (regcache, fner1_regnum, fpregsetp->fner[1]);
+
+ regcache_raw_supply (regcache, msr0_regnum, fpregsetp->msr[0]);
+ regcache_raw_supply (regcache, msr1_regnum, fpregsetp->msr[1]);
+
+ for (regi = acc0_regnum; regi <= acc7_regnum; regi++)
+ regcache_raw_supply (regcache, regi, fpregsetp->acc[regi - acc0_regnum]);
+
+ regcache_raw_supply (regcache, accg0123_regnum, fpregsetp->accg);
+ regcache_raw_supply (regcache, accg4567_regnum, fpregsetp->accg + 4);
+
+ regcache_raw_supply (regcache, fsr0_regnum, fpregsetp->fsr[0]);
+}
+
+/* FRV Linux kernel register sets. */
+
+static struct regset frv_linux_gregset =
+{
+ NULL,
+ frv_linux_supply_gregset
+};
+
+static struct regset frv_linux_fpregset =
+{
+ NULL,
+ frv_linux_supply_fpregset
+};
+
+static const struct regset *
+frv_linux_regset_from_core_section (struct gdbarch *gdbarch,
+ const char *sect_name, size_t sect_size)
+{
+ if (strcmp (sect_name, ".reg") == 0
+ && sect_size >= sizeof (frv_elf_gregset_t))
+ return &frv_linux_gregset;
+
+ if (strcmp (sect_name, ".reg2") == 0
+ && sect_size >= sizeof (frv_elf_fpregset_t))
+ return &frv_linux_fpregset;
+
+ return NULL;
+}
+
+\f
static void
frv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
- /* When the FR-V Linux kernel calls a signal handler, the return
- address points to a bit of code on the stack. This function is
- used to identify this bit of code as a signal trampoline in order
- to support backtracing through calls to signal handlers. */
- set_gdbarch_pc_in_sigtramp (gdbarch, frv_linux_pc_in_sigtramp);
- frv_set_sigcontext_reg_addr (gdbarch, frv_linux_sigcontext_reg_addr);
+ /* Set the sigtramp frame sniffer. */
+ frame_unwind_append_sniffer (gdbarch, frv_linux_sigtramp_frame_sniffer);
+ set_gdbarch_regset_from_core_section (gdbarch,
+ frv_linux_regset_from_core_section);
}
static enum gdb_osabi