Re: [PATCH] Renesas SH (sh4) native support

Yao Qi
2011-11-20T07:10:13Z

On 11/18/2011 07:20 AM, Hector Oron wrote:
>

You may also have to provide an entry in gdb/NEWS to mention this
change.

> --- gdb-7.4.orig/gdb/sh-linux-tdep.c
> +++ gdb-7.4/gdb/sh-linux-tdep.c
> @@ -19,11 +19,34 @@
> 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 <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"
> @@ -71,9 +94,505 @@ static const struct sh_corefile_regmap f
> {-1 /* Terminator. */, 0}
> };
>
> +/* 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;
> + }

braces are not needed here. We can,

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

^^
Need extra spaces around "+".

> + 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 ());

We should call get_frame_arch (this_frame) to get gdbarch instead of
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);

This statement above can be moved into "if block" below.

> + 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);

I am not familiar with SH, but "64" is magic number to me. We may
either add a comment here or replace this magic number with a macro.

> +}
> +
> +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)

Wrong indentation.

> +{
> + 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)

Wrong indentation, and other places below.

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

Can it be "static"?

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

We may need extra parentheses so that the indentation shows the
nesting, like this,

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

Spaces are need around "+" and "*", and below

> + }
> + 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[] =

static const int h_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[] =

static const.

> +{
> + 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);

Why do we need checking !gdbarch_regset_from_core_section_p
(gdbarch) ?

> +
> linux_init_abi (info, gdbarch);
>
> /* GNU/Linux uses SVR4-style shared libraries. */
> --- gdb-7.4.orig/gdb/sh-tdep.h
> +++ gdb-7.4/gdb/sh-tdep.h
> @@ -22,6 +22,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
> {
> @@ -30,6 +36,7 @@ enum
> ARG0_REGNUM = 4,
> ARGLAST_REGNUM = 7,
> FP_REGNUM = 14,
> + SP_REGNUM = 15,
> PC_REGNUM = 16,
> PR_REGNUM = 17,
> GBR_REGNUM = 18,
> @@ -83,8 +90,26 @@ enum
> 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 gdbarch_init_ftype sh64_gdbarch_init;
> extern void sh64_show_regs (struct frame_info *);
> +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
> @@ -93,8 +118,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;

First of all, I don't find this field is used in your patch. I
guess it is going to be used in get_longjmp_target gdbarch method.
Secondly, `jp_pc_offset' is only set to 32 in this patch, so it looks
like a constant to me. If the "pc offset in jump buffer" is an invariant,
we can use a const directly.

> +
> + /* 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. */
> --- gdb-7.4.orig/gdb/sh-tdep.c
> +++ gdb-7.4/gdb/sh-tdep.c
> @@ -2611,7 +2598,7 @@ sh_frame_prev_register (struct frame_inf
> 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? */

Why leave that comment here?

>
> if (regnum < SH_NUM_REGS && cache->saved_regs[regnum] != -1)
> return frame_unwind_got_memory (this_frame, regnum,
> @@ -2855,8 +2842,8 @@ sh_regset_from_core_section (struct gdba
> static struct gdbarch *
> sh_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
> {
> - struct gdbarch *gdbarch;
> struct gdbarch_tdep *tdep;
> + struct gdbarch *gdbarch;
>

Unnecessary change.

> sh_show_regs = sh_generic_show_regs;
> switch (info.bfd_arch_info->mach)
> @@ -2919,6 +2906,18 @@ sh_gdbarch_init (struct gdbarch_info inf
> tdep = XZALLOC (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);
> @@ -3064,10 +3063,11 @@ sh_gdbarch_init (struct gdbarch_info inf
> break;
> }
>
> + dwarf2_append_unwinders (gdbarch);
> +
> /* Hook in ABI-specific overrides, if they have been registered. */
> gdbarch_init_osabi (info, gdbarch);
>
> - dwarf2_append_unwinders (gdbarch);

Unnecessary changes.

> frame_unwind_append_unwinder (gdbarch, &sh_frame_unwind);
>
> return gdbarch;
> --- /dev/null
> +++ gdb-7.4/gdb/config/sh/nm-linux.h
> @@ -0,0 +1,54 @@
> +/* Native-dependent definitions for SuperH running Linux, for GDB.
> + Copyright 2004 Free Software Foundation, Inc.
^^^^^^
It should be 2011.

> +
> + This file is part of GDB.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 2 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 59 Temple Place - Suite 330,
> + Boston, MA 02111-1307, USA. */
> +
> +#ifndef NM_LINUX_H
> +#define NM_LINUX_H
> +
> +/* Get generic Linux native definitions. */
> +#include "config/nm-linux.h"
> +/* Support for the user area. */
> +
> +/* Return the size of the user struct. */
> +extern int kernel_u_size (void);
> +#define KERNEL_U_SIZE kernel_u_size()
> +
> +/* This is the amount to substract from u.u_ar0 to get the offset in
> + the core file of the register values. */
> +#define KERNEL_U_ADDR 0
> +
> +#define U_REGS_OFFSET 0
> +
> +extern CORE_ADDR register_u_addr (CORE_ADDR blockend, int regnum);
> +#define REGISTER_U_ADDR(addr, blockend, regnum) \
> + (addr) = register_u_addr (blockend, regnum)
> +
> +/* Override copies of {fetch,store}_inferior_registers in `infptrace.c'. */
> +#define FETCH_INFERIOR_REGISTERS
> +
> +/* Nevertheless, define CANNOT_{FETCH,STORE}_REGISTER, because we
> + might fall back on the code `infptrace.c' (well a copy of that code
> + in `sh-linux-nat.c' for now) and we can access only the
> + general-purpose registers in that way. */
> +extern int cannot_fetch_register (int regno);
> +extern int cannot_store_register (int regno);
> +#define CANNOT_FETCH_REGISTER(regno) cannot_fetch_register (regno)
> +#define CANNOT_STORE_REGISTER(regno) cannot_store_register (regno)
> +
> +#endif /* NM_LINUX_H */

--
Yao (é½å°§)