922 lines
28 KiB
Diff
922 lines
28 KiB
Diff
From 4e5507488068b3ea2ebf9c84c6c192c7bfd3be82 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
|
|
|