- /* Target-dependent code for GNU/Linux running on the Fujitsu FR-V,
- for GDB.
- Copyright (C) 2004-2015 Free Software Foundation, Inc.
- 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 3 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, see <http://www.gnu.org/licenses/>. */
- #include "defs.h"
- #include "gdbcore.h"
- #include "target.h"
- #include "frame.h"
- #include "osabi.h"
- #include "regcache.h"
- #include "elf-bfd.h"
- #include "elf/frv.h"
- #include "frv-tdep.h"
- #include "trad-frame.h"
- #include "frame-unwind.h"
- #include "regset.h"
- #include "linux-tdep.h"
- /* Define the size (in bytes) of an FR-V instruction. */
- static const int frv_instr_size = 4;
- enum {
- NORMAL_SIGTRAMP = 1,
- RT_SIGTRAMP = 2
- };
- static int
- frv_linux_pc_in_sigtramp (struct gdbarch *gdbarch, CORE_ADDR pc,
- const char *name)
- {
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- gdb_byte buf[frv_instr_size];
- LONGEST instr;
- int retval = 0;
- if (target_read_memory (pc, buf, sizeof buf) != 0)
- return 0;
- instr = extract_unsigned_integer (buf, sizeof buf, byte_order);
- if (instr == 0x8efc0077) /* setlos #__NR_sigreturn, gr7 */
- retval = NORMAL_SIGTRAMP;
- else if (instr == 0x8efc00ad) /* setlos #__NR_rt_sigreturn, gr7 */
- retval = RT_SIGTRAMP;
- else
- return 0;
- if (target_read_memory (pc + frv_instr_size, buf, sizeof buf) != 0)
- return 0;
- instr = extract_unsigned_integer (buf, sizeof buf, byte_order);
- if (instr != 0xc0700000) /* tira gr0, 0 */
- return 0;
- /* If we get this far, we'll return a non-zero value, either
- NORMAL_SIGTRAMP (1) or RT_SIGTRAMP (2). */
- return retval;
- }
- /* Given NEXT_FRAME, the "callee" frame of the sigtramp frame that we
- wish to decode, and REGNO, one of the frv register numbers defined
- in frv-tdep.h, return the address of the saved register (corresponding
- to REGNO) in the sigtramp frame. Return -1 if the register is not
- found in the sigtramp frame. The magic numbers in the code below
- were computed by examining the following kernel structs:
- From arch/frv/kernel/signal.c:
- struct sigframe
- {
- void (*pretcode)(void);
- int sig;
- struct sigcontext sc;
- unsigned long extramask[_NSIG_WORDS-1];
- uint32_t retcode[2];
- };
- struct rt_sigframe
- {
- void (*pretcode)(void);
- int sig;
- struct siginfo *pinfo;
- void *puc;
- struct siginfo info;
- struct ucontext uc;
- uint32_t retcode[2];
- };
- From include/asm-frv/ucontext.h:
- struct ucontext {
- unsigned long uc_flags;
- struct ucontext *uc_link;
- stack_t uc_stack;
- struct sigcontext uc_mcontext;
- sigset_t uc_sigmask;
- };
- From include/asm-frv/signal.h:
- typedef struct sigaltstack {
- void *ss_sp;
- int ss_flags;
- size_t ss_size;
- } stack_t;
- From include/asm-frv/sigcontext.h:
- struct sigcontext {
- struct user_context sc_context;
- unsigned long sc_oldmask;
- } __attribute__((aligned(8)));
- From include/asm-frv/registers.h:
- struct user_int_regs
- {
- unsigned long psr;
- unsigned long isr;
- unsigned long ccr;
- unsigned long cccr;
- unsigned long lr;
- unsigned long lcr;
- unsigned long pc;
- unsigned long __status;
- unsigned long syscallno;
- unsigned long orig_gr8;
- unsigned long gner[2];
- unsigned long long iacc[1];
- union {
- unsigned long tbr;
- unsigned long gr[64];
- };
- };
- struct user_fpmedia_regs
- {
- unsigned long fr[64];
- unsigned long fner[2];
- unsigned long msr[2];
- unsigned long acc[8];
- unsigned char accg[8];
- unsigned long fsr[1];
- };
- struct user_context
- {
- struct user_int_regs i;
- struct user_fpmedia_regs f;
- void *extension;
- } __attribute__((aligned(8))); */
- static LONGEST
- frv_linux_sigcontext_reg_addr (struct frame_info *this_frame, int regno,
- CORE_ADDR *sc_addr_cache_ptr)
- {
- struct gdbarch *gdbarch = get_frame_arch (this_frame);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- CORE_ADDR sc_addr;
- if (sc_addr_cache_ptr && *sc_addr_cache_ptr)
- {
- sc_addr = *sc_addr_cache_ptr;
- }
- else
- {
- CORE_ADDR pc, sp;
- gdb_byte buf[4];
- int tramp_type;
- pc = get_frame_pc (this_frame);
- tramp_type = frv_linux_pc_in_sigtramp (gdbarch, pc, 0);
- get_frame_register (this_frame, sp_regnum, buf);
- sp = extract_unsigned_integer (buf, sizeof buf, byte_order);
- if (tramp_type == NORMAL_SIGTRAMP)
- {
- /* For a normal sigtramp frame, the sigcontext struct starts
- at SP + 8. */
- sc_addr = sp + 8;
- }
- else if (tramp_type == RT_SIGTRAMP)
- {
- /* For a realtime sigtramp frame, SP + 12 contains a pointer
- to a ucontext struct. The ucontext struct contains a
- sigcontext struct starting 24 bytes in. (The offset of
- uc_mcontext within struct ucontext is derived as follows:
- stack_t is a 12-byte struct and struct sigcontext is
- 8-byte aligned. This gives an offset of 8 + 12 + 4 (for
- padding) = 24.) */
- if (target_read_memory (sp + 12, buf, sizeof buf) != 0)
- {
- warning (_("Can't read realtime sigtramp frame."));
- return 0;
- }
- sc_addr = extract_unsigned_integer (buf, sizeof buf, byte_order);
- sc_addr += 24;
- }
- else
- internal_error (__FILE__, __LINE__, _("not a signal trampoline"));
- if (sc_addr_cache_ptr)
- *sc_addr_cache_ptr = sc_addr;
- }
- switch (regno)
- {
- case psr_regnum :
- return sc_addr + 0;
- /* sc_addr + 4 has "isr", the Integer Status Register. */
- case ccr_regnum :
- return sc_addr + 8;
- case cccr_regnum :
- return sc_addr + 12;
- case lr_regnum :
- return sc_addr + 16;
- case lcr_regnum :
- return sc_addr + 20;
- case pc_regnum :
- return sc_addr + 24;
- /* sc_addr + 28 is __status, the exception status.
- sc_addr + 32 is syscallno, the syscall number or -1.
- sc_addr + 36 is orig_gr8, the original syscall arg #1.
- sc_addr + 40 is gner[0].
- sc_addr + 44 is gner[1]. */
- case iacc0h_regnum :
- return sc_addr + 48;
- case iacc0l_regnum :
- return sc_addr + 52;
- default :
- if (first_gpr_regnum <= regno && regno <= last_gpr_regnum)
- return sc_addr + 56 + 4 * (regno - first_gpr_regnum);
- else if (first_fpr_regnum <= regno && regno <= last_fpr_regnum)
- return sc_addr + 312 + 4 * (regno - first_fpr_regnum);
- else
- return -1; /* not saved. */
- }
- }
- /* Signal trampolines. */
- static struct trad_frame_cache *
- frv_linux_sigtramp_frame_cache (struct frame_info *this_frame,
- void **this_cache)
- {
- struct gdbarch *gdbarch = get_frame_arch (this_frame);
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- struct trad_frame_cache *cache;
- CORE_ADDR addr;
- gdb_byte buf[4];
- int regnum;
- CORE_ADDR sc_addr_cache_val = 0;
- struct frame_id this_id;
- if (*this_cache)
- return *this_cache;
- cache = trad_frame_cache_zalloc (this_frame);
- /* FIXME: cagney/2004-05-01: This is is long standing broken code.
- The frame ID's code address should be the start-address of the
- signal trampoline and not the current PC within that
- trampoline. */
- get_frame_register (this_frame, sp_regnum, buf);
- addr = extract_unsigned_integer (buf, sizeof buf, byte_order);
- this_id = frame_id_build (addr, get_frame_pc (this_frame));
- trad_frame_set_id (cache, this_id);
- for (regnum = 0; regnum < frv_num_regs; regnum++)
- {
- LONGEST reg_addr = frv_linux_sigcontext_reg_addr (this_frame, regnum,
- &sc_addr_cache_val);
- if (reg_addr != -1)
- trad_frame_set_reg_addr (cache, regnum, reg_addr);
- }
- *this_cache = cache;
- return cache;
- }
- static void
- frv_linux_sigtramp_frame_this_id (struct frame_info *this_frame,
- void **this_cache,
- struct frame_id *this_id)
- {
- struct trad_frame_cache *cache
- = frv_linux_sigtramp_frame_cache (this_frame, this_cache);
- trad_frame_get_id (cache, this_id);
- }
- static struct value *
- frv_linux_sigtramp_frame_prev_register (struct frame_info *this_frame,
- void **this_cache, int regnum)
- {
- /* Make sure we've initialized the cache. */
- struct trad_frame_cache *cache
- = frv_linux_sigtramp_frame_cache (this_frame, this_cache);
- return trad_frame_get_register (cache, this_frame, regnum);
- }
- static int
- frv_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
- struct frame_info *this_frame,
- void **this_cache)
- {
- struct gdbarch *gdbarch = get_frame_arch (this_frame);
- CORE_ADDR pc = get_frame_pc (this_frame);
- const char *name;
- find_pc_partial_function (pc, &name, NULL, NULL);
- if (frv_linux_pc_in_sigtramp (gdbarch, pc, name))
- return 1;
- return 0;
- }
- static const struct frame_unwind frv_linux_sigtramp_frame_unwind =
- {
- SIGTRAMP_FRAME,
- default_frame_unwind_stop_reason,
- frv_linux_sigtramp_frame_this_id,
- frv_linux_sigtramp_frame_prev_register,
- NULL,
- frv_linux_sigtramp_frame_sniffer
- };
- /* The FRV kernel defines ELF_NGREG as 46. We add 2 in order to include
- the loadmap addresses in the register set. (See below for more info.) */
- #define FRV_ELF_NGREG (46 + 2)
- typedef unsigned char frv_elf_greg_t[4];
- typedef struct { frv_elf_greg_t reg[FRV_ELF_NGREG]; } frv_elf_gregset_t;
- typedef unsigned char frv_elf_fpreg_t[4];
- typedef struct
- {
- frv_elf_fpreg_t fr[64];
- frv_elf_fpreg_t fner[2];
- frv_elf_fpreg_t msr[2];
- frv_elf_fpreg_t acc[8];
- unsigned char accg[8];
- frv_elf_fpreg_t fsr[1];
- } frv_elf_fpregset_t;
- /* Register maps. */
- static const struct regcache_map_entry frv_linux_gregmap[] =
- {
- { 1, psr_regnum, 4 },
- { 1, REGCACHE_MAP_SKIP, 4 }, /* isr */
- { 1, ccr_regnum, 4 },
- { 1, cccr_regnum, 4 },
- { 1, lr_regnum, 4 },
- { 1, lcr_regnum, 4 },
- { 1, pc_regnum, 4 },
- { 1, REGCACHE_MAP_SKIP, 4 }, /* __status */
- { 1, REGCACHE_MAP_SKIP, 4 }, /* syscallno */
- { 1, REGCACHE_MAP_SKIP, 4 }, /* orig_gr8 */
- { 1, gner0_regnum, 4 },
- { 1, gner1_regnum, 4 },
- { 1, REGCACHE_MAP_SKIP, 8 }, /* iacc0 */
- { 1, tbr_regnum, 4 },
- { 31, first_gpr_regnum + 1, 4 }, /* gr1 ... gr31 */
- /* Technically, the loadmap addresses are not part of `pr_reg' as
- found in the elf_prstatus struct. The fields which communicate
- the loadmap address appear (by design) immediately after
- `pr_reg' though, and the BFD function elf32_frv_grok_prstatus()
- has been implemented to include these fields in the register
- section that it extracts from the core file. So, for our
- purposes, they may be viewed as registers. */
- { 1, fdpic_loadmap_exec_regnum, 4 },
- { 1, fdpic_loadmap_interp_regnum, 4 },
- { 0 }
- };
- static const struct regcache_map_entry frv_linux_fpregmap[] =
- {
- { 64, first_fpr_regnum, 4 }, /* fr0 ... fr63 */
- { 1, fner0_regnum, 4 },
- { 1, fner1_regnum, 4 },
- { 1, msr0_regnum, 4 },
- { 1, msr1_regnum, 4 },
- { 8, acc0_regnum, 4 }, /* acc0 ... acc7 */
- { 1, accg0123_regnum, 4 },
- { 1, accg4567_regnum, 4 },
- { 1, fsr0_regnum, 4 },
- { 0 }
- };
- /* Unpack an frv_elf_gregset_t into GDB's register cache. */
- static void
- frv_linux_supply_gregset (const struct regset *regset,
- struct regcache *regcache,
- int regnum, const void *gregs, size_t len)
- {
- int regi;
- char zerobuf[MAX_REGISTER_SIZE];
- memset (zerobuf, 0, MAX_REGISTER_SIZE);
- /* gr0 always contains 0. Also, the kernel passes the TBR value in
- this slot. */
- regcache_raw_supply (regcache, first_gpr_regnum, zerobuf);
- /* Fill gr32, ..., gr63 with zeros. */
- for (regi = first_gpr_regnum + 32; regi <= last_gpr_regnum; regi++)
- regcache_raw_supply (regcache, regi, zerobuf);
- regcache_supply_regset (regset, regcache, regnum, gregs, len);
- }
- /* FRV Linux kernel register sets. */
- static const struct regset frv_linux_gregset =
- {
- frv_linux_gregmap,
- frv_linux_supply_gregset, regcache_collect_regset
- };
- static const struct regset frv_linux_fpregset =
- {
- frv_linux_fpregmap,
- regcache_supply_regset, regcache_collect_regset
- };
- static void
- frv_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
- iterate_over_regset_sections_cb *cb,
- void *cb_data,
- const struct regcache *regcache)
- {
- cb (".reg", sizeof (frv_elf_gregset_t), &frv_linux_gregset,
- NULL, cb_data);
- cb (".reg2", sizeof (frv_elf_fpregset_t), &frv_linux_fpregset,
- NULL, cb_data);
- }
- static void
- frv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
- {
- linux_init_abi (info, gdbarch);
- /* Set the sigtramp frame sniffer. */
- frame_unwind_append_unwinder (gdbarch, &frv_linux_sigtramp_frame_unwind);
- set_gdbarch_iterate_over_regset_sections
- (gdbarch, frv_linux_iterate_over_regset_sections);
- }
- static enum gdb_osabi
- frv_linux_elf_osabi_sniffer (bfd *abfd)
- {
- int elf_flags;
- elf_flags = elf_elfheader (abfd)->e_flags;
- /* Assume GNU/Linux if using the FDPIC ABI. If/when another OS shows
- up that uses this ABI, we'll need to start using .note sections
- or some such. */
- if (elf_flags & EF_FRV_FDPIC)
- return GDB_OSABI_LINUX;
- else
- return GDB_OSABI_UNKNOWN;
- }
- /* Provide a prototype to silence -Wmissing-prototypes. */
- void _initialize_frv_linux_tdep (void);
- void
- _initialize_frv_linux_tdep (void)
- {
- gdbarch_register_osabi (bfd_arch_frv, 0, GDB_OSABI_LINUX,
- frv_linux_init_abi);
- gdbarch_register_osabi_sniffer (bfd_arch_frv,
- bfd_target_elf_flavour,
- frv_linux_elf_osabi_sniffer);
- }