blob: 4a3e22c4458607ed0f80c245801deb0e81a04f32 [file] [log] [blame]
From 407cb13cfb70697f45dfb761304e005e1ecbd0e9 Mon Sep 17 00:00:00 2001
From: Khem Raj <raj.khem@gmail.com>
Date: Mon, 2 Mar 2015 02:31:12 +0000
Subject: [PATCH 3/3] Add support for Renesas SH (sh4) architecture.
gdb (7.4-1~cvs20111117.2) experimental; urgency=low
.
* Add Renesas SH (sh4) support (Closes: #576242)
- Thanks Nobuhiro Iwamatsu, Takashi Yoshii.
Author: Hector Oron <zumbi@debian.org>
Bug-Debian: http://bugs.debian.org/576242
Upstream-Status: Pending
Signed-off-by: Khem Raj <raj.khem@gmail.com>
---
gdb/Makefile.in | 1 +
gdb/configure.host | 1 +
gdb/sh-linux-tdep.c | 519 +++++++++++++++++++++++++++++++++++
gdb/sh-tdep.c | 54 ++--
gdb/sh-tdep.h | 49 ++++
gdb/testsuite/gdb.asm/asm-source.exp | 5 +
gdb/testsuite/gdb.asm/sh.inc | 3 +-
gdb/testsuite/gdb.base/annota1.c | 3 +
gdb/testsuite/gdb.base/annota3.c | 4 +
gdb/testsuite/gdb.base/sigall.c | 3 +
gdb/testsuite/gdb.base/signals.c | 4 +
11 files changed, 617 insertions(+), 29 deletions(-)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 7937801..63baf81 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1707,6 +1707,7 @@ ALLDEPFILES = \
score-tdep.c \
ser-go32.c ser-pipe.c ser-tcp.c ser-mingw.c \
sh-tdep.c sh64-tdep.c shnbsd-tdep.c shnbsd-nat.c \
+ sh-linux-tdep.c sh-linux-nat.c \
sol2-tdep.c \
solib-svr4.c \
sparc-linux-nat.c sparc-linux-tdep.c \
diff --git a/gdb/configure.host b/gdb/configure.host
index d07be4b..b6391c5 100644
--- a/gdb/configure.host
+++ b/gdb/configure.host
@@ -150,6 +150,7 @@ powerpc*-*-linux*) gdb_host=linux ;;
s390*-*-linux*) gdb_host=linux ;;
+sh*-*-linux*) gdb_host=linux ;;
sh*-*-netbsdelf* | sh*-*-knetbsd*-gnu)
gdb_host=nbsd ;;
sh*-*-openbsd*) gdb_host=nbsd ;;
diff --git a/gdb/sh-linux-tdep.c b/gdb/sh-linux-tdep.c
index 2ff2ee8..1a11262 100644
--- a/gdb/sh-linux-tdep.c
+++ b/gdb/sh-linux-tdep.c
@@ -18,14 +18,37 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
+#include "gdbcore.h"
+#include "frame.h"
+#include "frame-base.h"
+#include "frame-unwind.h"
+#include "dwarf2-frame.h"
+#include "value.h"
+#include "regcache.h"
+#include "inferior.h"
#include "osabi.h"
+#include "reggroups.h"
+#include "arch-utils.h"
+#include "floatformat.h"
#include "solib-svr4.h"
#include "symtab.h"
+#include "gdb_string.h"
+#include "command.h"
+#include "gdb_assert.h"
#include "trad-frame.h"
#include "tramp-frame.h"
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/user.h>
+#include <sys/syscall.h>
+
+#include <asm/ptrace.h>
+
+#include "regset.h"
#include "glibc-tdep.h"
#include "sh-tdep.h"
#include "linux-tdep.h"
@@ -180,9 +203,505 @@ static struct tramp_frame sh_linux_rt_sigreturn_tramp_frame = {
sh_linux_rt_sigreturn_init
};
+/* Recognizing signal handler frames. */
+
+/* GNU/Linux has two flavors of signals. Normal signal handlers, and
+ "realtime" (RT) signals. The RT signals can provide additional
+ information to the signal handler if the SA_SIGINFO flag is set
+ when establishing a signal handler using `sigaction'. It is not
+ unlikely that future versions of GNU/Linux will support SA_SIGINFO
+ for normal signals too. */
+
+/* When the SH Linux kernel calls a signal handler and the
+ SA_RESTORER flag isn't set, the return address points to a bit of
+ code on the stack. This function returns whether the PC appears to
+ be within this bit of code.
+
+ The instruction sequence for normal signals is
+ mov.w 1f,r3
+ trapa #16
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ 1: .word __NR_sigreturn
+ or 0x9305 0xc310 0x200b 0x200b 0x200b 0x200b 0x200b 0x0077.
+
+ Checking for the code sequence should be somewhat reliable, because
+ the effect is to call the system call sigreturn. This is unlikely
+ to occur anywhere other than a signal trampoline.
+
+ It kind of sucks that we have to read memory from the process in
+ order to identify a signal trampoline, but there doesn't seem to be
+ any other way. The PC_IN_SIGTRAMP macro in tm-linux.h arranges to
+ only call us if no function name could be identified, which should
+ be the case since the code is on the stack.
+
+ Detection of signal trampolines for handlers that set the
+ SA_RESTORER flag is in general not possible. Unfortunately this is
+ what the GNU C Library has been doing for quite some time now.
+ However, as of version 2.1.2, the GNU C Library uses signal
+ trampolines (named __restore and __restore_rt) that are identical
+ to the ones used by the kernel. Therefore, these trampolines are
+ supported too. */
+
+#define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */
+#define TRAP16 0xc310 /* Syscall w/no args (NR in R3) */
+#define OR_R0_R0 0x200b /* or r0,r0 (insert to avoid hardware bug) */
+
+#define LINUX_SIGTRAMP_INSN0 MOVW(7) /* Move mem word at PC+7 to R3 */
+#define LINUX_SIGTRAMP_INSN1 TRAP16 /* Syscall w/no args (NR in R3) */
+#define LINUX_SIGTRAMP_INSN2 OR_R0_R0 /* or r0,r0 (insert to avoid hardware bug) */
+
+static const unsigned short linux_sigtramp_code[] =
+{
+ LINUX_SIGTRAMP_INSN0,
+ LINUX_SIGTRAMP_INSN1,
+ LINUX_SIGTRAMP_INSN2,
+ LINUX_SIGTRAMP_INSN2,
+ LINUX_SIGTRAMP_INSN2,
+ LINUX_SIGTRAMP_INSN2,
+ LINUX_SIGTRAMP_INSN2,
+ __NR_sigreturn
+};
+
+#define LINUX_SIGTRAMP_LEN (sizeof linux_sigtramp_code)
+
+/* If PC is in a sigtramp routine, return the address of the start of
+ the routine. Otherwise, return 0. */
+
+static CORE_ADDR
+sh_linux_sigtramp_start (struct frame_info *next_frame)
+{
+ CORE_ADDR pc = get_frame_pc (next_frame);
+ gdb_byte buf[LINUX_SIGTRAMP_LEN];
+
+ /* We only recognize a signal trampoline if PC is at the start of
+ one of the three instructions. We optimize for finding the PC at
+ the start, as will be the case when the trampoline is not the
+ first frame on the stack. We assume that in the case where the
+ PC is not at the start of the instruction sequence, there will be
+ a few trailing readable bytes on the stack. */
+
+ if (!safe_frame_unwind_memory (next_frame, pc, buf, LINUX_SIGTRAMP_LEN))
+ return 0;
+
+ if (buf[0] != LINUX_SIGTRAMP_INSN0)
+ {
+ if (buf[0] != LINUX_SIGTRAMP_INSN1)
+ return 0;
+
+ pc -= 2;
+
+ if (!safe_frame_unwind_memory (next_frame, pc, buf, LINUX_SIGTRAMP_LEN))
+ return 0;
+ }
+
+ if (memcmp (buf, linux_sigtramp_code, LINUX_SIGTRAMP_LEN) != 0)
+ return 0;
+
+ return pc;
+}
+
+/* This function does the same for RT signals. Here the instruction
+ sequence is
+ mov.w 1f,r3
+ trapa #16
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ 1: .word __NR_rt_sigreturn
+ or 0x9305 0xc310 0x200b 0x200b 0x200b 0x200b 0x200b 0x00ad.
+
+ The effect is to call the system call rt_sigreturn. */
+
+#define LINUX_RT_SIGTRAMP_INSN0 MOVW(7) /* Move mem word at PC+7 to R3 */
+#define LINUX_RT_SIGTRAMP_INSN1 TRAP16 /* Syscall w/no args (NR in R3) */
+#define LINUX_RT_SIGTRAMP_INSN2 OR_R0_R0 /* or r0,r0 (insert to avoid hardware bug) */
+
+static const unsigned short linux_rt_sigtramp_code[] =
+{
+ LINUX_RT_SIGTRAMP_INSN0,
+ LINUX_RT_SIGTRAMP_INSN1,
+ LINUX_RT_SIGTRAMP_INSN2,
+ LINUX_RT_SIGTRAMP_INSN2,
+ LINUX_RT_SIGTRAMP_INSN2,
+ LINUX_RT_SIGTRAMP_INSN2,
+ LINUX_RT_SIGTRAMP_INSN2,
+ __NR_rt_sigreturn
+};
+
+#define LINUX_RT_SIGTRAMP_LEN (sizeof linux_rt_sigtramp_code)
+
+/* If PC is in a RT sigtramp routine, return the address of the start
+ of the routine. Otherwise, return 0. */
+
+static CORE_ADDR
+sh_linux_rt_sigtramp_start (struct frame_info *next_frame)
+{
+ CORE_ADDR pc = get_frame_pc (next_frame);
+ gdb_byte buf[LINUX_RT_SIGTRAMP_LEN];
+
+ /* We only recognize a signal trampoline if PC is at the start of
+ one of the two instructions. We optimize for finding the PC at
+ the start, as will be the case when the trampoline is not the
+ first frame on the stack. We assume that in the case where the
+ PC is not at the start of the instruction sequence, there will be
+ a few trailing readable bytes on the stack. */
+
+ if (!safe_frame_unwind_memory (next_frame, pc, buf, LINUX_RT_SIGTRAMP_LEN))
+ return 0;
+
+ if (buf[0] != LINUX_RT_SIGTRAMP_INSN0)
+ {
+ if (buf[0] != LINUX_RT_SIGTRAMP_INSN1)
+ return 0;
+
+ pc -= 2;
+
+ if (!safe_frame_unwind_memory (next_frame, pc, buf,
+ LINUX_RT_SIGTRAMP_LEN))
+ return 0;
+ }
+
+ if (memcmp (buf, linux_rt_sigtramp_code, LINUX_RT_SIGTRAMP_LEN) != 0)
+ return 0;
+
+ return pc;
+}
+
+/* Return whether PC is in a GNU/Linux sigtramp routine. */
+
+static int
+sh_linux_sigtramp_p (struct frame_info *this_frame)
+{
+ CORE_ADDR pc = get_frame_pc (this_frame);
+ char *name;
+
+ find_pc_partial_function (pc, &name, NULL, NULL);
+
+ /* If we have NAME, we can optimize the search. The trampolines are
+ named __restore and __restore_rt. However, they aren't dynamically
+ exported from the shared C library, so the trampoline may appear to
+ be part of the preceding function. This should always be sigaction,
+ __sigaction, or __libc_sigaction (all aliases to the same function). */
+ if (name == NULL || strstr (name, "sigaction") != NULL)
+ return (sh_linux_sigtramp_start (this_frame) != 0
+ || sh_linux_rt_sigtramp_start (this_frame) != 0);
+
+ return (strcmp ("__restore", name) == 0
+ || strcmp ("__restore_rt", name) == 0);
+}
+
+/* Offset to struct sigcontext in ucontext, from <asm/ucontext.h>. */
+#define SH_LINUX_UCONTEXT_SIGCONTEXT_OFFSET 12
+
+
+/* Assuming NEXT_FRAME is a frame following a GNU/Linux sigtramp
+ routine, return the address of the associated sigcontext structure. */
+
+static CORE_ADDR
+sh_linux_sigcontext_addr (struct frame_info *this_frame)
+{
+ CORE_ADDR pc;
+ CORE_ADDR sp;
+
+ sp = get_frame_register_unsigned (this_frame, SP_REGNUM);
+
+ pc = sh_linux_sigtramp_start (this_frame);
+ if (pc)
+ {
+ return sp;
+ }
+
+ pc = sh_linux_rt_sigtramp_start (this_frame);
+ if (pc)
+ {
+ CORE_ADDR ucontext_addr;
+
+ /* The sigcontext structure is part of the user context. A
+ pointer to the user context is passed as the third argument
+ to the signal handler. */
+ ucontext_addr = get_frame_register_unsigned (this_frame, ARG0_REGNUM+2);
+ return ucontext_addr + SH_LINUX_UCONTEXT_SIGCONTEXT_OFFSET;
+ }
+
+ error ("Couldn't recognize signal trampoline.");
+ return 0;
+}
+
+/* Signal trampolines. */
+extern struct sh_frame_cache *sh_alloc_frame_cache (void);
+
+static struct sh_frame_cache *
+sh_linux_sigtramp_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+ struct sh_frame_cache *cache;
+ struct gdbarch_tdep *tdep = gdbarch_tdep (get_current_arch ());
+ CORE_ADDR sigcontext_addr;
+
+ if (*this_cache)
+ return *this_cache;
+
+ cache = sh_alloc_frame_cache ();
+
+ cache->base = get_frame_register_unsigned (this_frame, SP_REGNUM);
+ sigcontext_addr = tdep->sigcontext_addr (this_frame);
+ if (tdep->sc_reg_offset)
+ {
+ int i;
+
+ gdb_assert (tdep->sc_num_regs <= SH_NUM_REGS);
+
+ for (i = 0; i < tdep->sc_num_regs; i++)
+ if (tdep->sc_reg_offset[i] != -1)
+ cache->saved_regs[i] = sigcontext_addr + tdep->sc_reg_offset[i];
+ }
+
+ *this_cache = cache;
+ return cache;
+}
+
+static void
+sh_linux_sigtramp_frame_this_id (struct frame_info *this_frame, void **this_cache,
+ struct frame_id *this_id)
+{
+ struct sh_frame_cache *cache =
+ sh_linux_sigtramp_frame_cache (this_frame, this_cache);
+
+ (*this_id) = frame_id_build (cache->base + 64, cache->pc);
+}
+
+extern struct value * sh_frame_prev_register ();
+static struct value *
+sh_linux_sigtramp_frame_prev_register (struct frame_info *this_frame,
+ void **this_cache, int regnum)
+{
+ sh_linux_sigtramp_frame_cache (this_frame, this_cache);
+
+ return sh_frame_prev_register (this_frame, this_cache, regnum);
+}
+
+static int
+sh_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
+ struct frame_info *this_frame,
+ void **this_prologue_cache)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (this_frame));
+
+ /* We shouldn't even bother if we don't have a sigcontext_addr
+ handler. */
+ if (tdep->sigcontext_addr == NULL)
+ return 0;
+
+ if (tdep->sigtramp_p != NULL)
+ {
+ if (tdep->sigtramp_p (this_frame))
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct frame_unwind sh_linux_sigtramp_frame_unwind =
+{
+ SIGTRAMP_FRAME,
+ sh_linux_sigtramp_frame_this_id,
+ sh_linux_sigtramp_frame_prev_register,
+ NULL,
+ sh_linux_sigtramp_frame_sniffer
+};
+
+/* Supply register REGNUM from the buffer specified by GREGS and LEN
+ in the general-purpose register set REGSET to register cache
+ REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */
+
+void
+sh_supply_gregset (const struct regset *regset, struct regcache *regcache,
+ int regnum, const void *gregs, size_t len)
+{
+ const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
+ const char *regs = gregs;
+ int i;
+
+ gdb_assert (len == tdep->sizeof_gregset);
+
+ for (i = 0; i < tdep->gregset_num_regs; i++)
+ {
+ if ((regnum == i || regnum == -1)
+ && tdep->gregset_reg_offset[i] != -1)
+ regcache_raw_supply (regcache, i, regs + tdep->gregset_reg_offset[i]);
+ }
+}
+
+/* Collect register REGNUM from the register cache REGCACHE and store
+ it in the buffer specified by GREGS and LEN as described by the
+ general-purpose register set REGSET. If REGNUM is -1, do this for
+ all registers in REGSET. */
+
+void
+sh_collect_gregset (const struct regset *regset,
+ const struct regcache *regcache,
+ int regnum, void *gregs, size_t len)
+{
+ const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
+ char *regs = gregs;
+ int i;
+
+ gdb_assert (len == tdep->sizeof_gregset);
+
+ for (i = 0; i < tdep->gregset_num_regs; i++)
+ {
+ if ((regnum == i || regnum == -1)
+ && tdep->gregset_reg_offset[i] != -1)
+ regcache_raw_collect (regcache, i, regs + tdep->gregset_reg_offset[i]);
+ }
+}
+
+/* Supply register REGNUM from the buffer specified by FPREGS and LEN
+ in the floating-point register set REGSET to register cache
+ REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */
+
+static void
+sh_supply_fpregset (const struct regset *regset, struct regcache *regcache,
+ int regnum, const void *fpregs, size_t len)
+{
+ const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
+ const char *regs = fpregs;
+ int i;
+
+ gdb_assert (len == tdep->sizeof_fpregset);
+ for (i = 0; i < 16; i++)
+ {
+ if (regnum == i+25 || regnum == -1)
+ regcache_raw_supply (regcache, i+25, regs + i*4);
+ }
+ if (regnum == FPSCR_REGNUM || regnum == -1)
+ regcache_raw_supply (regcache, FPSCR_REGNUM, regs + 32*4);
+ if (regnum == FPUL_REGNUM || regnum == -1)
+ regcache_raw_supply (regcache, FPUL_REGNUM, regs + 33*4);
+}
+
+/* Collect register REGNUM from the register cache REGCACHE and store
+ it in the buffer specified by FPREGS and LEN as described by the
+ floating-point register set REGSET. If REGNUM is -1, do this for
+ all registers in REGSET. */
+
+static void
+sh_collect_fpregset (const struct regset *regset,
+ const struct regcache *regcache,
+ int regnum, void *fpregs, size_t len)
+{
+ const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
+ char *regs = fpregs;
+ int i;
+
+ gdb_assert (len == tdep->sizeof_fpregset);
+ for (i = 0; i < 16; i++)
+ {
+ if (regnum == i+25 || regnum == -1)
+ regcache_raw_collect (regcache, i+25, regs + i*4);
+ }
+ if (regnum == FPSCR_REGNUM || regnum == -1)
+ regcache_raw_collect (regcache, FPSCR_REGNUM, regs + 32*4);
+ if (regnum == FPUL_REGNUM || regnum == -1)
+ regcache_raw_collect (regcache, FPUL_REGNUM, regs + 33*4);
+}
+
+/* Return the appropriate register set for the core section identified
+ by SECT_NAME and SECT_SIZE. */
+
+const struct regset *
+sh_linux_regset_from_core_section (struct gdbarch *gdbarch,
+ const char *sect_name, size_t sect_size)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (strcmp (sect_name, ".reg") == 0 && sect_size == tdep->sizeof_gregset)
+ {
+ if (tdep->gregset == NULL)
+ tdep->gregset = regset_alloc (gdbarch, sh_supply_gregset,
+ sh_collect_gregset);
+ return tdep->gregset;
+ }
+
+ if ((strcmp (sect_name, ".reg2") == 0 && sect_size == tdep->sizeof_fpregset))
+ {
+ if (tdep->fpregset == NULL)
+ tdep->fpregset = regset_alloc (gdbarch, sh_supply_fpregset,
+ sh_collect_fpregset);
+ return tdep->fpregset;
+ }
+
+ return NULL;
+}
+
+/* The register sets used in GNU/Linux ELF core-dumps are identical to
+ the register sets in `struct user' that are used for a.out
+ core-dumps. These are also used by ptrace(2). The corresponding
+ types are `elf_gregset_t' for the general-purpose registers (with
+ `elf_greg_t' the type of a single GP register) and `elf_fpregset_t'
+ for the floating-point registers.
+
+ Those types used to be available under the names `gregset_t' and
+ `fpregset_t' too, and GDB used those names in the past. But those
+ names are now used for the register sets used in the `mcontext_t'
+ type, which have a different size and layout. */
+
+/* Mapping between the general-purpose registers in `struct user'
+ format and GDB's register cache layout. */
+
+/* From <sys/reg.h>. */
+static int sh_linux_gregset_reg_offset[] =
+{
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+
+ REG_PC*4, REG_PR*4, REG_GBR*4, -1,
+ REG_MACH*4, REG_MACL*4, REG_SR*4,
+};
+
+/* Mapping between the general-purpose registers in `struct
+ sigcontext' format and GDB's register cache layout. */
+
+/* From <asm/sigcontext.h>. */
+static int sh_linux_sc_reg_offset[] =
+{
+ 4, 8, 12, 16, 20, 24, 28, 32,
+ 36, 40, 44, 48, 52, 56, 60, 64,
+ 68, 72, 80, -1,
+ 84, 88, 76
+};
+
static void
sh_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ bfd abfd;
+
+ tdep->gregset_reg_offset = sh_linux_gregset_reg_offset;
+ tdep->gregset_num_regs = ARRAY_SIZE (sh_linux_gregset_reg_offset);
+ tdep->sizeof_gregset = 23 * 4;
+
+ tdep->jb_pc_offset = 32; /* From <bits/setjmp.h>. */
+
+ tdep->sigtramp_p = sh_linux_sigtramp_p;
+ tdep->sigcontext_addr = sh_linux_sigcontext_addr;
+ tdep->sc_reg_offset = sh_linux_sc_reg_offset;
+ tdep->sc_num_regs = ARRAY_SIZE (sh_linux_sc_reg_offset);
+
+ frame_unwind_append_unwinder(gdbarch, &sh_linux_sigtramp_frame_unwind);
+
+ /* If we have a register mapping, enable the generic core file
+ support, unless it has already been enabled. */
+ if (tdep->gregset_reg_offset
+ && !gdbarch_regset_from_core_section_p (gdbarch))
+ set_gdbarch_regset_from_core_section (gdbarch,
+ sh_linux_regset_from_core_section);
+
linux_init_abi (info, gdbarch);
/* GNU/Linux uses SVR4-style shared libraries. */
diff --git a/gdb/sh-tdep.c b/gdb/sh-tdep.c
index 82cf2f4..b443e46 100644
--- a/gdb/sh-tdep.c
+++ b/gdb/sh-tdep.c
@@ -21,6 +21,9 @@
sac@cygnus.com. */
#include "defs.h"
+#include "arch-utils.h"
+#include "command.h"
+#include "dummy-frame.h"
#include "frame.h"
#include "frame-base.h"
#include "frame-unwind.h"
@@ -35,6 +38,7 @@
#include "arch-utils.h"
#include "floatformat.h"
#include "regcache.h"
+#include "regset.h"
#include "doublest.h"
#include "osabi.h"
#include "reggroups.h"
@@ -67,23 +71,6 @@ static const char *const sh_cc_enum[] = {
static const char *sh_active_calling_convention = sh_cc_gcc;
-#define SH_NUM_REGS 67
-
-struct sh_frame_cache
-{
- /* Base address. */
- CORE_ADDR base;
- LONGEST sp_offset;
- CORE_ADDR pc;
-
- /* Flag showing that a frame has been created in the prologue code. */
- int uses_fp;
-
- /* Saved registers. */
- CORE_ADDR saved_regs[SH_NUM_REGS];
- CORE_ADDR saved_sp;
-};
-
static int
sh_is_renesas_calling_convention (struct type *func_type)
{
@@ -1043,7 +1030,7 @@ sh_treat_as_flt_p (struct type *type)
return 0;
/* Otherwise if the type of that member is float, the whole type is
treated as float. */
- if (TYPE_CODE (TYPE_FIELD_TYPE (type, 0)) == TYPE_CODE_FLT)
+ if (TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type, 0))) == TYPE_CODE_FLT)
return 1;
/* Otherwise it's not treated as float. */
return 0;
@@ -1093,7 +1080,7 @@ sh_push_dummy_call_fpu (struct gdbarch *gdbarch,
in four registers available. Loop thru args from first to last. */
for (argnum = 0; argnum < nargs; argnum++)
{
- type = value_type (args[argnum]);
+ type = check_typedef (value_type (args[argnum]));
len = TYPE_LENGTH (type);
val = sh_justify_value_in_reg (gdbarch, args[argnum], len);
@@ -1821,7 +1808,7 @@ sh_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
reg->how = DWARF2_FRAME_REG_UNDEFINED;
}
-static struct sh_frame_cache *
+struct sh_frame_cache *
sh_alloc_frame_cache (void)
{
struct sh_frame_cache *cache;
@@ -1848,7 +1835,7 @@ sh_alloc_frame_cache (void)
return cache;
}
-static struct sh_frame_cache *
+struct sh_frame_cache *
sh_frame_cache (struct frame_info *this_frame, void **this_cache)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
@@ -1915,9 +1902,9 @@ sh_frame_cache (struct frame_info *this_frame, void **this_cache)
return cache;
}
-static struct value *
-sh_frame_prev_register (struct frame_info *this_frame,
- void **this_cache, int regnum)
+struct value *
+sh_frame_prev_register (struct frame_info *this_frame, void **this_cache,
+ int regnum)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
struct sh_frame_cache *cache = sh_frame_cache (this_frame, this_cache);
@@ -1931,7 +1918,7 @@ sh_frame_prev_register (struct frame_info *this_frame,
the current frame. Frob regnum so that we pull the value from
the correct place. */
if (regnum == gdbarch_pc_regnum (gdbarch))
- regnum = PR_REGNUM;
+ regnum = PR_REGNUM; /* XXX: really? */
if (regnum < SH_NUM_REGS && cache->saved_regs[regnum] != -1)
return frame_unwind_got_memory (this_frame, regnum,
@@ -2237,8 +2224,8 @@ sh_return_in_first_hidden_param_p (struct gdbarch *gdbarch,
static struct gdbarch *
sh_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
- struct gdbarch *gdbarch;
struct gdbarch_tdep *tdep;
+ struct gdbarch *gdbarch;
/* SH5 is handled entirely in sh64-tdep.c. */
if (info.bfd_arch_info->mach == bfd_mach_sh5)
@@ -2254,6 +2241,18 @@ sh_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep = XCNEW (struct gdbarch_tdep);
gdbarch = gdbarch_alloc (&info, tdep);
+ /* General-purpose registers. */
+ tdep->gregset = NULL;
+ tdep->gregset_reg_offset = NULL;
+ tdep->gregset_num_regs = 23;
+ tdep->sizeof_gregset = 0;
+
+ /* Floating-point registers. */
+ tdep->fpregset = NULL;
+ tdep->sizeof_fpregset = 34*4;
+
+ tdep->jb_pc_offset = -1;
+
set_gdbarch_short_bit (gdbarch, 2 * TARGET_CHAR_BIT);
set_gdbarch_int_bit (gdbarch, 4 * TARGET_CHAR_BIT);
set_gdbarch_long_bit (gdbarch, 4 * TARGET_CHAR_BIT);
@@ -2404,10 +2403,11 @@ sh_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
break;
}
+ dwarf2_append_unwinders (gdbarch);
+
/* Hook in ABI-specific overrides, if they have been registered. */
gdbarch_init_osabi (info, gdbarch);
- dwarf2_append_unwinders (gdbarch);
frame_unwind_append_unwinder (gdbarch, &sh_stub_unwind);
frame_unwind_append_unwinder (gdbarch, &sh_frame_unwind);
diff --git a/gdb/sh-tdep.h b/gdb/sh-tdep.h
index fc671a5..699f59f 100644
--- a/gdb/sh-tdep.h
+++ b/gdb/sh-tdep.h
@@ -21,6 +21,12 @@
/* Contributed by Steve Chamberlain sac@cygnus.com. */
+struct frame_info;
+struct gdbarch;
+struct reggroup;
+struct regset;
+struct regcache;
+
/* Registers for all SH variants. Used also by sh3-rom.c. */
enum
{
@@ -29,6 +35,7 @@ enum
ARG0_REGNUM = 4,
ARGLAST_REGNUM = 7,
FP_REGNUM = 14,
+ SP_REGNUM = 15,
PC_REGNUM = 16,
PR_REGNUM = 17,
GBR_REGNUM = 18,
@@ -81,6 +88,24 @@ enum
FV0_REGNUM = 76,
FV_LAST_REGNUM = 79
};
+#define SH_NUM_REGS 67
+
+struct sh_frame_cache
+{
+ /* Base address. */
+ CORE_ADDR base;
+ LONGEST sp_offset;
+ CORE_ADDR pc;
+
+ /* Flag showing that a frame has been created in the prologue code. */
+ int uses_fp;
+
+ /* Saved registers. */
+ CORE_ADDR saved_regs[SH_NUM_REGS];
+ CORE_ADDR saved_sp;
+};
+
+extern struct sh_frame_cache *sh_frame_cache (struct frame_info *next_frame, void **this_cache);
/* This structure describes a register in a core-file. */
struct sh_corefile_regmap
@@ -89,8 +114,32 @@ struct sh_corefile_regmap
unsigned int offset;
};
+/* sh architecture specific information. */
struct gdbarch_tdep
{
+ /* General-purpose registers. */
+ struct regset *gregset;
+ int *gregset_reg_offset;
+ int gregset_num_regs;
+ size_t sizeof_gregset;
+
+ /* Floating-point registers. */
+ struct regset *fpregset;
+ size_t sizeof_fpregset;
+
+ /* Offset of saved PC in jmp_buf. */
+ int jb_pc_offset;
+
+ /* Detect sigtramp. */
+ int (*sigtramp_p) (struct frame_info *);
+
+ /* Get address of sigcontext for sigtramp. */
+ CORE_ADDR (*sigcontext_addr) (struct frame_info *);
+
+ /* Offset of registers in `struct sigcontext'. */
+ int *sc_reg_offset;
+ int sc_num_regs;
+
/* Non-NULL when debugging from a core file. Provides the offset
where each general-purpose register is stored inside the associated
core file section. */
diff --git a/gdb/testsuite/gdb.asm/asm-source.exp b/gdb/testsuite/gdb.asm/asm-source.exp
index 8854af0..ef44682 100644
--- a/gdb/testsuite/gdb.asm/asm-source.exp
+++ b/gdb/testsuite/gdb.asm/asm-source.exp
@@ -113,6 +113,11 @@ switch -glob -- [istarget] {
append link-flags " -m elf32ppc"
}
}
+ "sh*-linux*" {
+ set asm-arch sh-linux
+ set asm-flags "-I${srcdir}/${subdir} -I${objdir}/${subdir}"
+ set debug-flags "-gdwarf-2"
+ }
"sh*-*-*" {
set asm-arch sh
set debug-flags "-gdwarf-2"
diff --git a/gdb/testsuite/gdb.asm/sh.inc b/gdb/testsuite/gdb.asm/sh.inc
index a4a5fc5..89efed7 100644
--- a/gdb/testsuite/gdb.asm/sh.inc
+++ b/gdb/testsuite/gdb.asm/sh.inc
@@ -40,9 +40,8 @@
mov.l .Lconst\@,r1
bra .Lafterconst\@
nop
- nop
-.Lconst\@:
.align 2
+.Lconst\@:
.long \subr
.align 1
.Lafterconst\@:
diff --git a/gdb/testsuite/gdb.base/annota1.c b/gdb/testsuite/gdb.base/annota1.c
index 424e1b8..0de2e7b 100644
--- a/gdb/testsuite/gdb.base/annota1.c
+++ b/gdb/testsuite/gdb.base/annota1.c
@@ -1,6 +1,9 @@
#include <stdio.h>
#include <signal.h>
+#ifdef __sh__
+#define signal(a,b) /* Signals not supported on this target - make them go away */
+#endif
void
handle_USR1 (int sig)
diff --git a/gdb/testsuite/gdb.base/annota3.c b/gdb/testsuite/gdb.base/annota3.c
index 424e1b8..952aaf2 100644
--- a/gdb/testsuite/gdb.base/annota3.c
+++ b/gdb/testsuite/gdb.base/annota3.c
@@ -1,6 +1,10 @@
#include <stdio.h>
#include <signal.h>
+#ifdef __sh__
+#define signal(a,b) /* Signals not supported on this target - make them go away */
+#endif
+
void
handle_USR1 (int sig)
diff --git a/gdb/testsuite/gdb.base/sigall.c b/gdb/testsuite/gdb.base/sigall.c
index 81f3b08..1574b2d 100644
--- a/gdb/testsuite/gdb.base/sigall.c
+++ b/gdb/testsuite/gdb.base/sigall.c
@@ -1,6 +1,9 @@
#include <signal.h>
#include <unistd.h>
+#ifdef __sh__
+#define signal(a,b) /* Signals not supported on this target - make them go away */
+#endif
/* Signal handlers, we set breakpoints in them to make sure that the
signals really get delivered. */
diff --git a/gdb/testsuite/gdb.base/signals.c b/gdb/testsuite/gdb.base/signals.c
index 7566068..1205a9b 100644
--- a/gdb/testsuite/gdb.base/signals.c
+++ b/gdb/testsuite/gdb.base/signals.c
@@ -3,6 +3,10 @@
#include <signal.h>
#include <unistd.h>
+#ifdef __sh__
+#define signal(a,b) /* Signals not supported on this target - make them go away */
+#define alarm(a) /* Ditto for alarm() */
+#endif
static int count = 0;
--
2.1.4