| 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 |
| |