1/* Target-dependent code for OpenBSD/sparc64.
2
3   Copyright 2004 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place - Suite 330,
20   Boston, MA 02111-1307, USA.  */
21
22#include "defs.h"
23#include "frame.h"
24#include "frame-unwind.h"
25#include "osabi.h"
26#include "regset.h"
27#include "symtab.h"
28#include "objfiles.h"
29#include "solib-svr4.h"
30#include "trad-frame.h"
31
32#include "gdb_assert.h"
33
34#include "sparc64-tdep.h"
35
36/* OpenBSD uses the traditional NetBSD core file format, even for
37   ports that use ELF.  The core files don't use multiple register
38   sets.  Instead, the general-purpose and floating-point registers
39   are lumped together in a single section.  Unlike on NetBSD, OpenBSD
40   uses a different layout for its general-purpose registers than the
41   layout used for ptrace(2).  */
42
43/* From <machine/reg.h>.  */
44const struct sparc_gregset sparc64obsd_core_gregset =
45{
46  0 * 8,			/* "tstate" */
47  1 * 8,			/* %pc */
48  2 * 8,			/* %npc */
49  3 * 8,			/* %y */
50  -1,				/* %fprs */
51  -1,
52  7 * 8,			/* %g1 */
53  22 * 8,			/* %l0 */
54  4				/* sizeof (%y) */
55};
56
57static void
58sparc64obsd_supply_gregset (const struct regset *regset,
59			    struct regcache *regcache,
60			    int regnum, const void *gregs, size_t len)
61{
62  const char *regs = gregs;
63
64  sparc64_supply_gregset (&sparc64obsd_core_gregset, regcache, regnum, regs);
65  sparc64_supply_fpregset (regcache, regnum, regs + 288);
66}
67
68
69/* Signal trampolines.  */
70
71/* The OpenBSD kernel maps the signal trampoline at some random
72   location in user space, which means that the traditional BSD way of
73   detecting it won't work.
74
75   The signal trampoline will be mapped at an address that is page
76   aligned.  We recognize the signal trampoline by the looking for the
77   sigreturn system call.  The offset where we can find the code that
78   makes this system call varies from release to release.  For OpenBSD
79   3.6 and later releases we can find the code at offset 0xec.  For
80   OpenBSD 3.5 and earlier releases, we find it at offset 0xe8.  */
81
82static const int sparc64obsd_page_size = 8192;
83static const int sparc64obsd_sigreturn_offset[] = { 0xec, 0xe8, -1 };
84
85static int
86sparc64obsd_pc_in_sigtramp (CORE_ADDR pc, char *name)
87{
88  CORE_ADDR start_pc = (pc & ~(sparc64obsd_page_size - 1));
89  unsigned long insn;
90  const int *offset;
91
92  if (name)
93    return 0;
94
95  for (offset = sparc64obsd_sigreturn_offset; *offset != -1; offset++)
96    {
97      /* Check for "restore %g0, SYS_sigreturn, %g1".  */
98      insn = sparc_fetch_instruction (start_pc + *offset);
99      if (insn != 0x83e82067)
100	continue;
101
102      /* Check for "t ST_SYSCALL".  */
103      insn = sparc_fetch_instruction (start_pc + *offset + 8);
104      if (insn != 0x91d02000)
105	continue;
106
107      return 1;
108    }
109
110  return 0;
111}
112
113static struct sparc_frame_cache *
114sparc64obsd_frame_cache (struct frame_info *next_frame, void **this_cache)
115{
116  struct sparc_frame_cache *cache;
117  CORE_ADDR addr;
118
119  if (*this_cache)
120    return *this_cache;
121
122  cache = sparc_frame_cache (next_frame, this_cache);
123  gdb_assert (cache == *this_cache);
124
125  /* If we couldn't find the frame's function, we're probably dealing
126     with an on-stack signal trampoline.  */
127  if (cache->pc == 0)
128    {
129      cache->pc = frame_pc_unwind (next_frame);
130      cache->pc &= ~(sparc64obsd_page_size - 1);
131
132      /* Since we couldn't find the frame's function, the cache was
133         initialized under the assumption that we're frameless.  */
134      cache->frameless_p = 0;
135      addr = frame_unwind_register_unsigned (next_frame, SPARC_FP_REGNUM);
136      cache->base = addr;
137    }
138
139  /* We find the appropriate instance of `struct sigcontext' at a
140     fixed offset in the signal frame.  */
141  addr = cache->base + BIAS + 128 + 16;
142  cache->saved_regs = sparc64nbsd_sigcontext_saved_regs (addr, next_frame);
143
144  return cache;
145}
146
147static void
148sparc64obsd_frame_this_id (struct frame_info *next_frame, void **this_cache,
149			   struct frame_id *this_id)
150{
151  struct sparc_frame_cache *cache =
152    sparc64obsd_frame_cache (next_frame, this_cache);
153
154  (*this_id) = frame_id_build (cache->base, cache->pc);
155}
156
157static void
158sparc64obsd_frame_prev_register (struct frame_info *next_frame,
159				 void **this_cache,
160				 int regnum, int *optimizedp,
161				 enum lval_type *lvalp, CORE_ADDR *addrp,
162				 int *realnump, void *valuep)
163{
164  struct sparc_frame_cache *cache =
165    sparc64obsd_frame_cache (next_frame, this_cache);
166
167  trad_frame_get_prev_register (next_frame, cache->saved_regs, regnum,
168				optimizedp, lvalp, addrp, realnump, valuep);
169}
170
171static const struct frame_unwind sparc64obsd_frame_unwind =
172{
173  SIGTRAMP_FRAME,
174  sparc64obsd_frame_this_id,
175  sparc64obsd_frame_prev_register
176};
177
178static const struct frame_unwind *
179sparc64obsd_sigtramp_frame_sniffer (struct frame_info *next_frame)
180{
181  CORE_ADDR pc = frame_pc_unwind (next_frame);
182  char *name;
183
184  find_pc_partial_function (pc, &name, NULL, NULL);
185  if (sparc64obsd_pc_in_sigtramp (pc, name))
186    return &sparc64obsd_frame_unwind;
187
188  return NULL;
189}
190
191
192static void
193sparc64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
194{
195  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
196
197  tdep->gregset = regset_alloc (gdbarch, sparc64obsd_supply_gregset, NULL);
198  tdep->sizeof_gregset = 832;
199
200  frame_unwind_append_sniffer (gdbarch, sparc64obsd_sigtramp_frame_sniffer);
201
202  sparc64_init_abi (info, gdbarch);
203
204  /* OpenBSD/sparc64 has SVR4-style shared libraries...  */
205  set_gdbarch_in_solib_call_trampoline (gdbarch, in_plt_section);
206  set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
207  set_solib_svr4_fetch_link_map_offsets
208    (gdbarch, svr4_lp64_fetch_link_map_offsets);
209}
210
211
212/* Provide a prototype to silence -Wmissing-prototypes.  */
213void _initialize_sparc64obsd_tdep (void);
214
215void
216_initialize_sparc64obsd_tdep (void)
217{
218  gdbarch_register_osabi (bfd_arch_sparc, bfd_mach_sparc_v9,
219			  GDB_OSABI_OPENBSD_ELF, sparc64obsd_init_abi);
220}
221