From 5ecb7103b5a8ba2208afe56b7ce17fd57090285a Mon Sep 17 00:00:00 2001 From: Kevin Buettner Date: Mon, 15 Mar 2004 19:42:25 +0000 Subject: [PATCH] * Makefile.in (frv-linux-tdep.o): Add dependencies. * frv-linux-tdep.c: New file. * frv-tdep.c (struct gdbarch_tdep): Add new field ``sigcontext_reg_addr''. (frv_set_sigcontext_reg_addr, frv_sigtramp,frame_cache) (frv_sigtramp_frame_this_id, frv_sigtramp_frame_prev_register) (frv_sigramp_frame_sniffer): New functions. (frv_sigtramp_frame_unwind): New static global. (frv_gdbarch_init): Hook in ABI-specific overrides. Hook up frame sniffers. * frv-tdep.h (frv_set_sigcontext_reg_addr): New function. * config/frv/frv.mt (TDEPFILES): Add frv-linux-tdep.o. --- gdb/ChangeLog | 15 +++ gdb/Makefile.in | 2 + gdb/config/frv/frv.mt | 2 +- gdb/frv-linux-tdep.c | 273 ++++++++++++++++++++++++++++++++++++++++++ gdb/frv-tdep.c | 121 ++++++++++++++++++- gdb/frv-tdep.h | 5 + 6 files changed, 416 insertions(+), 2 deletions(-) create mode 100644 gdb/frv-linux-tdep.c diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 4561d2fcd65..42351545f3f 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,18 @@ +2004-03-15 Kevin Buettner + + * Makefile.in (frv-linux-tdep.o): Add dependencies. + * frv-linux-tdep.c: New file. + * frv-tdep.c (struct gdbarch_tdep): Add new field + ``sigcontext_reg_addr''. + (frv_set_sigcontext_reg_addr, frv_sigtramp,frame_cache) + (frv_sigtramp_frame_this_id, frv_sigtramp_frame_prev_register) + (frv_sigramp_frame_sniffer): New functions. + (frv_sigtramp_frame_unwind): New static global. + (frv_gdbarch_init): Hook in ABI-specific overrides. Hook up frame + sniffers. + * frv-tdep.h (frv_set_sigcontext_reg_addr): New function. + * config/frv/frv.mt (TDEPFILES): Add frv-linux-tdep.o. + 2004-03-15 Kevin Buettner * frv-tdep.c (frv_analyze_prologue): Terminate prologue scan, diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 143ecd0da4d..ab571af9ef9 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1766,6 +1766,8 @@ frame.o: frame.c $(defs_h) $(frame_h) $(target_h) $(value_h) $(inferior_h) \ $(command_h) $(gdbcmd_h) frame-unwind.o: frame-unwind.c $(defs_h) $(frame_h) $(frame_unwind_h) \ $(gdb_assert_h) $(dummy_frame_h) +frv-linux-tdep.o: frv-linux-tdep.c $(defs_h) $(target_h) $(osabi_h) \ + $(elf_bfd_h) $(elf_frv_h) $(frv_tdep_h) frv-tdep.o: frv-tdep.c $(defs_h) $(gdb_string_h) $(inferior_h) $(gdbcore_h) \ $(arch_utils_h) $(regcache_h) $(frame_h) $(frame_unwind_h) \ $(frame_base_h) $(trad_frame_h) $(dis_asm_h) $(gdb_assert_h) \ diff --git a/gdb/config/frv/frv.mt b/gdb/config/frv/frv.mt index d7829154b56..5849f0413c3 100644 --- a/gdb/config/frv/frv.mt +++ b/gdb/config/frv/frv.mt @@ -1,5 +1,5 @@ # Target: Fujitsu FRV processor -TDEPFILES= frv-tdep.o solib.o solib-frv.o +TDEPFILES= frv-tdep.o frv-linux-tdep.o solib.o solib-frv.o TM_FILE= tm-frv.h SIM_OBS = remote-sim.o SIM = ../sim/frv/libsim.a 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); +} diff --git a/gdb/frv-tdep.c b/gdb/frv-tdep.c index ffd911e0997..48d241c2bf4 100644 --- a/gdb/frv-tdep.c +++ b/gdb/frv-tdep.c @@ -96,6 +96,11 @@ struct gdbarch_tdep /* Register names. */ char **register_names; + + /* Given NEXT_FRAME, determine the address of register REGNO saved in + the calling sigtramp frame. */ + CORE_ADDR (*sigcontext_reg_addr) (struct frame_info *next_frame, int regno, + CORE_ADDR *); }; #define CURRENT_VARIANT (gdbarch_tdep (current_gdbarch)) @@ -107,6 +112,15 @@ frv_abi (struct gdbarch *gdbarch) return gdbarch_tdep (gdbarch)->frv_abi; } +/* Set sigcontext_reg_addr. */ +void +frv_set_sigcontext_reg_addr (struct gdbarch *gdbarch, + CORE_ADDR (*sigcontext_reg_addr) + (struct frame_info *, int, CORE_ADDR *)) +{ + gdbarch_tdep (gdbarch)->sigcontext_reg_addr = sigcontext_reg_addr; +} + /* Fetch the interpreter and executable loadmap addresses (for shared library support) for the FDPIC ABI. Return 0 if successful, -1 if not. (E.g, -1 will be returned if the ABI isn't the FDPIC ABI.) */ @@ -1344,6 +1358,101 @@ frv_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame) frame_pc_unwind (next_frame)); } +/* Signal trampolines. */ + +static struct frv_unwind_cache * +frv_sigtramp_frame_cache (struct frame_info *next_frame, void **this_cache) +{ + struct frv_unwind_cache *cache; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + CORE_ADDR addr; + char buf[4]; + int regno; + CORE_ADDR sc_addr_cache_val = 0; + + if (*this_cache) + return *this_cache; + + cache = FRAME_OBSTACK_ZALLOC (struct frv_unwind_cache); + cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); + + frame_unwind_register (next_frame, sp_regnum, buf); + cache->base = extract_unsigned_integer (buf, sizeof buf); + + for (regno = 0; regno < frv_num_regs; regno++) + { + cache->saved_regs[regno].addr + = tdep->sigcontext_reg_addr (next_frame, regno, &sc_addr_cache_val); + } + + + if (cache->saved_regs[sp_regnum].addr != -1 + && target_read_memory (cache->saved_regs[sp_regnum].addr, + buf, sizeof buf) == 0) + { + cache->prev_sp = extract_unsigned_integer (buf, sizeof buf); + + /* Now that we've bothered to read it out of memory, save the + prev frame's SP value in the cache. */ + trad_frame_set_value (cache->saved_regs, sp_regnum, cache->prev_sp); + } + else + { + warning ("Can't read SP value from sigtramp frame"); + } + + *this_cache = cache; + return cache; +} + +static void +frv_sigtramp_frame_this_id (struct frame_info *next_frame, void **this_cache, + struct frame_id *this_id) +{ + struct frv_unwind_cache *cache = + frv_sigtramp_frame_cache (next_frame, this_cache); + + (*this_id) = frame_id_build (cache->base, frame_pc_unwind (next_frame)); +} + +static void +frv_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. */ + frv_sigtramp_frame_cache (next_frame, this_cache); + + frv_frame_prev_register (next_frame, this_cache, regnum, + optimizedp, lvalp, addrp, realnump, valuep); +} + +static const struct frame_unwind frv_sigtramp_frame_unwind = +{ + SIGTRAMP_FRAME, + frv_sigtramp_frame_this_id, + frv_sigtramp_frame_prev_register +}; + +static const struct frame_unwind * +frv_sigtramp_frame_sniffer (struct frame_info *next_frame) +{ + CORE_ADDR pc = frame_pc_unwind (next_frame); + char *name; + + /* We shouldn't even bother to try if the OSABI didn't register + a sigcontext_reg_addr handler. */ + if (!gdbarch_tdep (current_gdbarch)->sigcontext_reg_addr) + return NULL; + + find_pc_partial_function (pc, &name, NULL, NULL); + if (PC_IN_SIGTRAMP (pc, name)) + return &frv_sigtramp_frame_unwind; + + return NULL; +} static struct gdbarch * frv_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) @@ -1434,8 +1543,9 @@ frv_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_unwind_pc (gdbarch, frv_unwind_pc); set_gdbarch_unwind_sp (gdbarch, frv_unwind_sp); set_gdbarch_frame_align (gdbarch, frv_frame_align); - frame_unwind_append_sniffer (gdbarch, frv_frame_sniffer); frame_base_set_default (gdbarch, &frv_frame_base); + /* We set the sniffer lower down after the OSABI hooks have been + established. */ /* Settings for calling functions in the inferior. */ set_gdbarch_push_dummy_call (gdbarch, frv_push_dummy_call); @@ -1480,6 +1590,15 @@ frv_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_convert_from_func_ptr_addr (gdbarch, frv_convert_from_func_ptr_addr); + /* Hook in ABI-specific overrides, if they have been registered. */ + gdbarch_init_osabi (info, gdbarch); + + /* Set the sigtramp frame sniffer. */ + frame_unwind_append_sniffer (gdbarch, frv_sigtramp_frame_sniffer); + + /* Set the fallback (prologue based) frame sniffer. */ + frame_unwind_append_sniffer (gdbarch, frv_frame_sniffer); + return gdbarch; } diff --git a/gdb/frv-tdep.h b/gdb/frv-tdep.h index e33fb117cf5..5b9b88b93a5 100644 --- a/gdb/frv-tdep.h +++ b/gdb/frv-tdep.h @@ -84,6 +84,11 @@ enum { /* Return the FR-V ABI associated with GDBARCH. */ enum frv_abi frv_abi (struct gdbarch *gdbarch); +/* Associate a sigcontext address fetcher with GDBARCH. */ +void frv_set_sigcontext_reg_addr (struct gdbarch *gdbarch, + CORE_ADDR (*sigcontext_reg_addr) + (struct frame_info *, int, CORE_ADDR *)); + /* Fetch the interpreter and executable loadmap addresses (for shared library support) for the FDPIC ABI. Return 0 if successful, -1 if not. (E.g, -1 will be returned if the ABI isn't the FDPIC ABI.) */ -- 2.30.2