diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2004-12-27 13:17:06 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2004-12-27 13:17:06 +0000 |
commit | 2ed3fb102e82ad2127a78a905dce5acf55afc729 (patch) | |
tree | b55832f88f0f339bfb56693aead72749739d66df /gnu/usr.bin/binutils/gdb/frv-linux-tdep.c | |
parent | bafd719ae34a32376be8ae2ddd673e95254092b4 (diff) |
GDB 6.3 (excluding .info files)
Diffstat (limited to 'gnu/usr.bin/binutils/gdb/frv-linux-tdep.c')
-rw-r--r-- | gnu/usr.bin/binutils/gdb/frv-linux-tdep.c | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/gnu/usr.bin/binutils/gdb/frv-linux-tdep.c b/gnu/usr.bin/binutils/gdb/frv-linux-tdep.c new file mode 100644 index 00000000000..b365915fc81 --- /dev/null +++ b/gnu/usr.bin/binutils/gdb/frv-linux-tdep.c @@ -0,0 +1,353 @@ +/* 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" +#include "trad-frame.h" +#include "frame-unwind.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 LONGEST +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. */ + } +} + +/* Signal trampolines. */ + +static struct trad_frame_cache * +frv_linux_sigtramp_frame_cache (struct frame_info *next_frame, void **this_cache) +{ + struct trad_frame_cache *cache; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + CORE_ADDR addr; + char 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 (next_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. */ + frame_unwind_register (next_frame, sp_regnum, buf); + this_id = frame_id_build (extract_unsigned_integer (buf, sizeof buf), + frame_pc_unwind (next_frame)); + trad_frame_set_id (cache, this_id); + + for (regnum = 0; regnum < frv_num_regs; regnum++) + { + LONGEST reg_addr = frv_linux_sigcontext_reg_addr (next_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 *next_frame, void **this_cache, + struct frame_id *this_id) +{ + struct trad_frame_cache *cache = + frv_linux_sigtramp_frame_cache (next_frame, this_cache); + trad_frame_get_id (cache, this_id); +} + +static void +frv_linux_sigtramp_frame_prev_register (struct frame_info *next_frame, + void **this_cache, + int regnum, int *optimizedp, + enum lval_type *lvalp, CORE_ADDR *addrp, + int *realnump, void *valuep) +{ + /* Make sure we've initialized the cache. */ + struct trad_frame_cache *cache = + frv_linux_sigtramp_frame_cache (next_frame, this_cache); + trad_frame_get_register (cache, next_frame, regnum, optimizedp, lvalp, + addrp, realnump, valuep); +} + +static const struct frame_unwind frv_linux_sigtramp_frame_unwind = +{ + SIGTRAMP_FRAME, + frv_linux_sigtramp_frame_this_id, + frv_linux_sigtramp_frame_prev_register +}; + +static const struct frame_unwind * +frv_linux_sigtramp_frame_sniffer (struct frame_info *next_frame) +{ + CORE_ADDR pc = frame_pc_unwind (next_frame); + char *name; + + find_pc_partial_function (pc, &name, NULL, NULL); + if (frv_linux_pc_in_sigtramp (pc, name)) + return &frv_linux_sigtramp_frame_unwind; + + return NULL; +} + +static void +frv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + /* Set the sigtramp frame sniffer. */ + frame_unwind_append_sniffer (gdbarch, frv_linux_sigtramp_frame_sniffer); +} + +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); +} |