| /* Target-dependent code for OpenBSD/sparc. | 
 |  | 
 |    Copyright (C) 2004-2014 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 "floatformat.h" | 
 | #include "frame.h" | 
 | #include "frame-unwind.h" | 
 | #include "gdbcore.h" | 
 | #include "osabi.h" | 
 | #include "regcache.h" | 
 | #include "symtab.h" | 
 | #include "trad-frame.h" | 
 |  | 
 | #include "gdb_assert.h" | 
 |  | 
 | #include "obsd-tdep.h" | 
 | #include "sparc-tdep.h" | 
 | #include "solib-svr4.h" | 
 | #include "bsd-uthread.h" | 
 |  | 
 | /* Signal trampolines.  */ | 
 |  | 
 | /* The OpenBSD kernel maps the signal trampoline at some random | 
 |    location in user space, which means that the traditional BSD way of | 
 |    detecting it won't work. | 
 |  | 
 |    The signal trampoline will be mapped at an address that is page | 
 |    aligned.  We recognize the signal trampoline by looking for the | 
 |    sigreturn system call.  */ | 
 |  | 
 | static const int sparc32obsd_page_size = 4096; | 
 |  | 
 | static int | 
 | sparc32obsd_pc_in_sigtramp (CORE_ADDR pc, const char *name) | 
 | { | 
 |   CORE_ADDR start_pc = (pc & ~(sparc32obsd_page_size - 1)); | 
 |   unsigned long insn; | 
 |  | 
 |   if (name) | 
 |     return 0; | 
 |  | 
 |   /* Check for "restore %g0, SYS_sigreturn, %g1".  */ | 
 |   insn = sparc_fetch_instruction (start_pc + 0xec); | 
 |   if (insn != 0x83e82067) | 
 |     return 0; | 
 |  | 
 |   /* Check for "t ST_SYSCALL".  */ | 
 |   insn = sparc_fetch_instruction (start_pc + 0xf4); | 
 |   if (insn != 0x91d02000) | 
 |     return 0; | 
 |  | 
 |   return 1; | 
 | } | 
 |  | 
 | static struct sparc_frame_cache * | 
 | sparc32obsd_sigtramp_frame_cache (struct frame_info *this_frame, | 
 | 				  void **this_cache) | 
 | { | 
 |   struct sparc_frame_cache *cache; | 
 |   CORE_ADDR addr; | 
 |  | 
 |   if (*this_cache) | 
 |     return *this_cache; | 
 |  | 
 |   cache = sparc_frame_cache (this_frame, this_cache); | 
 |   gdb_assert (cache == *this_cache); | 
 |  | 
 |   /* If we couldn't find the frame's function, we're probably dealing | 
 |      with an on-stack signal trampoline.  */ | 
 |   if (cache->pc == 0) | 
 |     { | 
 |       cache->pc = get_frame_pc (this_frame); | 
 |       cache->pc &= ~(sparc32obsd_page_size - 1); | 
 |  | 
 |       /* Since we couldn't find the frame's function, the cache was | 
 |          initialized under the assumption that we're frameless.  */ | 
 |       sparc_record_save_insn (cache); | 
 |       addr = get_frame_register_unsigned (this_frame, SPARC_FP_REGNUM); | 
 |       cache->base = addr; | 
 |     } | 
 |  | 
 |   cache->saved_regs = sparc32nbsd_sigcontext_saved_regs (this_frame); | 
 |  | 
 |   return cache; | 
 | } | 
 |  | 
 | static void | 
 | sparc32obsd_sigtramp_frame_this_id (struct frame_info *this_frame, | 
 | 				    void **this_cache, | 
 | 				    struct frame_id *this_id) | 
 | { | 
 |   struct sparc_frame_cache *cache = | 
 |     sparc32obsd_sigtramp_frame_cache (this_frame, this_cache); | 
 |  | 
 |   (*this_id) = frame_id_build (cache->base, cache->pc); | 
 | } | 
 |  | 
 | static struct value * | 
 | sparc32obsd_sigtramp_frame_prev_register (struct frame_info *this_frame, | 
 | 					  void **this_cache, int regnum) | 
 | { | 
 |   struct sparc_frame_cache *cache = | 
 |     sparc32obsd_sigtramp_frame_cache (this_frame, this_cache); | 
 |  | 
 |   return trad_frame_get_prev_register (this_frame, cache->saved_regs, regnum); | 
 | } | 
 |  | 
 | static int | 
 | sparc32obsd_sigtramp_frame_sniffer (const struct frame_unwind *self, | 
 | 				    struct frame_info *this_frame, | 
 | 				    void **this_cache) | 
 | { | 
 |   CORE_ADDR pc = get_frame_pc (this_frame); | 
 |   const char *name; | 
 |  | 
 |   find_pc_partial_function (pc, &name, NULL, NULL); | 
 |   if (sparc32obsd_pc_in_sigtramp (pc, name)) | 
 |     return 1; | 
 |  | 
 |   return 0; | 
 | } | 
 | static const struct frame_unwind sparc32obsd_sigtramp_frame_unwind = | 
 | { | 
 |   SIGTRAMP_FRAME, | 
 |   default_frame_unwind_stop_reason, | 
 |   sparc32obsd_sigtramp_frame_this_id, | 
 |   sparc32obsd_sigtramp_frame_prev_register, | 
 |   NULL, | 
 |   sparc32obsd_sigtramp_frame_sniffer | 
 | }; | 
 |  | 
 |  | 
 |  | 
 | /* Offset wthin the thread structure where we can find %fp and %i7.  */ | 
 | #define SPARC32OBSD_UTHREAD_FP_OFFSET	128 | 
 | #define SPARC32OBSD_UTHREAD_PC_OFFSET	132 | 
 |  | 
 | static void | 
 | sparc32obsd_supply_uthread (struct regcache *regcache, | 
 | 			    int regnum, CORE_ADDR addr) | 
 | { | 
 |   struct gdbarch *gdbarch = get_regcache_arch (regcache); | 
 |   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | 
 |   CORE_ADDR fp, fp_addr = addr + SPARC32OBSD_UTHREAD_FP_OFFSET; | 
 |   gdb_byte buf[4]; | 
 |  | 
 |   gdb_assert (regnum >= -1); | 
 |  | 
 |   fp = read_memory_unsigned_integer (fp_addr, 4, byte_order); | 
 |   if (regnum == SPARC_SP_REGNUM || regnum == -1) | 
 |     { | 
 |       store_unsigned_integer (buf, 4, byte_order, fp); | 
 |       regcache_raw_supply (regcache, SPARC_SP_REGNUM, buf); | 
 |  | 
 |       if (regnum == SPARC_SP_REGNUM) | 
 | 	return; | 
 |     } | 
 |  | 
 |   if (regnum == SPARC32_PC_REGNUM || regnum == SPARC32_NPC_REGNUM | 
 |       || regnum == -1) | 
 |     { | 
 |       CORE_ADDR i7, i7_addr = addr + SPARC32OBSD_UTHREAD_PC_OFFSET; | 
 |  | 
 |       i7 = read_memory_unsigned_integer (i7_addr, 4, byte_order); | 
 |       if (regnum == SPARC32_PC_REGNUM || regnum == -1) | 
 | 	{ | 
 | 	  store_unsigned_integer (buf, 4, byte_order, i7 + 8); | 
 | 	  regcache_raw_supply (regcache, SPARC32_PC_REGNUM, buf); | 
 | 	} | 
 |       if (regnum == SPARC32_NPC_REGNUM || regnum == -1) | 
 | 	{ | 
 | 	  store_unsigned_integer (buf, 4, byte_order, i7 + 12); | 
 | 	  regcache_raw_supply (regcache, SPARC32_NPC_REGNUM, buf); | 
 | 	} | 
 |  | 
 |       if (regnum == SPARC32_PC_REGNUM || regnum == SPARC32_NPC_REGNUM) | 
 | 	return; | 
 |     } | 
 |  | 
 |   sparc_supply_rwindow (regcache, fp, regnum); | 
 | } | 
 |  | 
 | static void | 
 | sparc32obsd_collect_uthread(const struct regcache *regcache, | 
 | 			    int regnum, CORE_ADDR addr) | 
 | { | 
 |   struct gdbarch *gdbarch = get_regcache_arch (regcache); | 
 |   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | 
 |   CORE_ADDR sp; | 
 |   gdb_byte buf[4]; | 
 |  | 
 |   gdb_assert (regnum >= -1); | 
 |  | 
 |   if (regnum == SPARC_SP_REGNUM || regnum == -1) | 
 |     { | 
 |       CORE_ADDR fp_addr = addr + SPARC32OBSD_UTHREAD_FP_OFFSET; | 
 |  | 
 |       regcache_raw_collect (regcache, SPARC_SP_REGNUM, buf); | 
 |       write_memory (fp_addr,buf, 4); | 
 |     } | 
 |  | 
 |   if (regnum == SPARC32_PC_REGNUM || regnum == -1) | 
 |     { | 
 |       CORE_ADDR i7, i7_addr = addr + SPARC32OBSD_UTHREAD_PC_OFFSET; | 
 |  | 
 |       regcache_raw_collect (regcache, SPARC32_PC_REGNUM, buf); | 
 |       i7 = extract_unsigned_integer (buf, 4, byte_order) - 8; | 
 |       write_memory_unsigned_integer (i7_addr, 4, byte_order, i7); | 
 |  | 
 |       if (regnum == SPARC32_PC_REGNUM) | 
 | 	return; | 
 |     } | 
 |  | 
 |   regcache_raw_collect (regcache, SPARC_SP_REGNUM, buf); | 
 |   sp = extract_unsigned_integer (buf, 4, byte_order); | 
 |   sparc_collect_rwindow (regcache, sp, regnum); | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | sparc32obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | 
 | { | 
 |   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | 
 |  | 
 |   /* OpenBSD/sparc is very similar to NetBSD/sparc ELF.  */ | 
 |   sparc32nbsd_elf_init_abi (info, gdbarch); | 
 |  | 
 |   set_gdbarch_skip_solib_resolver (gdbarch, obsd_skip_solib_resolver); | 
 |  | 
 |   frame_unwind_append_unwinder (gdbarch, &sparc32obsd_sigtramp_frame_unwind); | 
 |  | 
 |   /* OpenBSD provides a user-level threads implementation.  */ | 
 |   bsd_uthread_set_supply_uthread (gdbarch, sparc32obsd_supply_uthread); | 
 |   bsd_uthread_set_collect_uthread (gdbarch, sparc32obsd_collect_uthread); | 
 | } | 
 |  | 
 |  | 
 | /* Provide a prototype to silence -Wmissing-prototypes.  */ | 
 | void _initialize_sparc32obsd_tdep (void); | 
 |  | 
 | void | 
 | _initialize_sparc32obsd_tdep (void) | 
 | { | 
 |   gdbarch_register_osabi (bfd_arch_sparc, 0, GDB_OSABI_OPENBSD_ELF, | 
 | 			  sparc32obsd_init_abi); | 
 | } |