diff options
Diffstat (limited to 'gdb/frv-linux-tdep.c')
-rw-r--r-- | gdb/frv-linux-tdep.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/gdb/frv-linux-tdep.c b/gdb/frv-linux-tdep.c new file mode 100644 index 00000000000..8588cb1882c --- /dev/null +++ b/gdb/frv-linux-tdep.c @@ -0,0 +1,273 @@ +/* Target-dependent code for GNU/Linux running on the Fujitsu FR-V, + for GDB. + Copyright 2004 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 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. */ + +#include "defs.h" +#include "target.h" +#include "frame.h" +#include "osabi.h" +#include "elf-bfd.h" +#include "elf/frv.h" +#include "frv-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 (CORE_ADDR pc, char *name) +{ + char 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); + + 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); + 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, "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/frvnommu/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-frvnommu/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-frvnommu/sigcontext.h: + + struct sigcontext { + struct user_context sc_context; + unsigned long sc_oldmask; + } __attribute__((aligned(8))); + + From include/asm-frvnommu/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 CORE_ADDR +frv_linux_sigcontext_reg_addr (struct frame_info *next_frame, int regno, + CORE_ADDR *sc_addr_cache_ptr) +{ + CORE_ADDR sc_addr; + + if (sc_addr_cache_ptr && *sc_addr_cache_ptr) + { + sc_addr = *sc_addr_cache_ptr; + } + else + { + CORE_ADDR pc, sp; + char buf[4]; + int tramp_type; + + pc = frame_pc_unwind (next_frame); + tramp_type = frv_linux_pc_in_sigtramp (pc, 0); + + frame_unwind_register (next_frame, sp_regnum, buf); + sp = extract_unsigned_integer (buf, sizeof buf); + + 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 the a ucontext struct. The ucontext struct contains + a sigcontext struct starting 12 bytes in. */ + 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); + sc_addr += 12; + } + 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. */ + } +} + +static void +frv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + /* When the FR-V Linux kernel calls a signal handler, the return + address points to a bit of code on the stack. This function is + used to identify this bit of code as a signal trampoline in order + to support backtracing through calls to signal handlers. */ + set_gdbarch_pc_in_sigtramp (gdbarch, frv_linux_pc_in_sigtramp); + frv_set_sigcontext_reg_addr (gdbarch, frv_linux_sigcontext_reg_addr); +} + +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); +} |