/* Target-dependent code for GNU/Linux running on the Fujitsu FR-V, for GDB. Copyright (C) 2004-2023 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 . */ #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" #include "gdbarch.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 (frame_info_ptr 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 (_("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 (frame_info_ptr this_frame, void **this_cache) { struct gdbarch *gdbarch = get_frame_arch (this_frame); 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 (struct trad_frame_cache *) *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 (frame_info_ptr 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 (frame_info_ptr 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, frame_info_ptr 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 = { "frv linux sigtramp", 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; /* gr0 always contains 0. Also, the kernel passes the TBR value in this slot. */ regcache->raw_supply_zeroed (first_gpr_regnum); /* Fill gr32, ..., gr63 with zeros. */ for (regi = first_gpr_regnum + 32; regi <= last_gpr_regnum; regi++) regcache->raw_supply_zeroed (regi); 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), sizeof (frv_elf_gregset_t), &frv_linux_gregset, NULL, cb_data); cb (".reg2", sizeof (frv_elf_fpregset_t), 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, 0); /* 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; } void _initialize_frv_linux_tdep (); void _initialize_frv_linux_tdep () { 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); }