1/* Target-dependent code for NetBSD/Alpha.
2
3   Copyright 2002, 2003, 2004 Free Software Foundation, Inc.
4   Contributed by Wasabi Systems, Inc.
5
6   This file is part of GDB.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place - Suite 330,
21   Boston, MA 02111-1307, USA.  */
22
23#include "defs.h"
24#include "gdbcore.h"
25#include "frame.h"
26#include "regcache.h"
27#include "value.h"
28#include "osabi.h"
29
30#include "solib-svr4.h"
31
32#include "alpha-tdep.h"
33#include "alphabsd-tdep.h"
34#include "nbsd-tdep.h"
35
36static void
37fetch_core_registers (char *core_reg_sect, unsigned core_reg_size, int which,
38                      CORE_ADDR ignore)
39{
40  char *regs, *fpregs;
41  int regno;
42
43  /* Table to map a gdb register number to a trapframe register index.  */
44  static const int regmap[] =
45  {
46     0,   1,   2,   3,
47     4,   5,   6,   7,
48     8,   9,  10,  11,
49    12,  13,  14,  15,
50    30,  31,  32,  16,
51    17,  18,  19,  20,
52    21,  22,  23,  24,
53    25,  29,  26
54  };
55#define SIZEOF_TRAPFRAME (33 * 8)
56
57  /* We get everything from one section.  */
58  if (which != 0)
59    return;
60
61  regs = core_reg_sect;
62  fpregs = core_reg_sect + SIZEOF_TRAPFRAME;
63
64  if (core_reg_size < (SIZEOF_TRAPFRAME + SIZEOF_STRUCT_FPREG))
65    {
66      warning ("Wrong size register set in core file.");
67      return;
68    }
69
70  /* Integer registers.  */
71  for (regno = 0; regno < ALPHA_ZERO_REGNUM; regno++)
72    supply_register (regno, regs + (regmap[regno] * 8));
73  supply_register (ALPHA_ZERO_REGNUM, NULL);
74  supply_register (PC_REGNUM, regs + (28 * 8));
75
76  /* Floating point registers.  */
77  alphabsd_supply_fpreg (fpregs, -1);
78}
79
80static void
81fetch_elfcore_registers (char *core_reg_sect, unsigned core_reg_size, int which,
82                         CORE_ADDR ignore)
83{
84  switch (which)
85    {
86    case 0:  /* Integer registers.  */
87      if (core_reg_size != SIZEOF_STRUCT_REG)
88	warning ("Wrong size register set in core file.");
89      else
90	alphabsd_supply_reg (core_reg_sect, -1);
91      break;
92
93    case 2:  /* Floating point registers.  */
94      if (core_reg_size != SIZEOF_STRUCT_FPREG)
95	warning ("Wrong size FP register set in core file.");
96      else
97	alphabsd_supply_fpreg (core_reg_sect, -1);
98      break;
99
100    default:
101      /* Don't know what kind of register request this is; just ignore it.  */
102      break;
103    }
104}
105
106static struct core_fns alphanbsd_core_fns =
107{
108  bfd_target_unknown_flavour,		/* core_flavour */
109  default_check_format,			/* check_format */
110  default_core_sniffer,			/* core_sniffer */
111  fetch_core_registers,			/* core_read_registers */
112  NULL					/* next */
113};
114
115static struct core_fns alphanbsd_elfcore_fns =
116{
117  bfd_target_elf_flavour,		/* core_flavour */
118  default_check_format,			/* check_format */
119  default_core_sniffer,			/* core_sniffer */
120  fetch_elfcore_registers,		/* core_read_registers */
121  NULL					/* next */
122};
123
124/* Under NetBSD/alpha, signal handler invocations can be identified by the
125   designated code sequence that is used to return from a signal handler.
126   In particular, the return address of a signal handler points to the
127   following code sequence:
128
129	ldq	a0, 0(sp)
130	lda	sp, 16(sp)
131	lda	v0, 295(zero)	# __sigreturn14
132	call_pal callsys
133
134   Each instruction has a unique encoding, so we simply attempt to match
135   the instruction the PC is pointing to with any of the above instructions.
136   If there is a hit, we know the offset to the start of the designated
137   sequence and can then check whether we really are executing in the
138   signal trampoline.  If not, -1 is returned, otherwise the offset from the
139   start of the return sequence is returned.  */
140static const unsigned char sigtramp_retcode[] =
141{
142  0x00, 0x00, 0x1e, 0xa6,	/* ldq a0, 0(sp) */
143  0x10, 0x00, 0xde, 0x23,	/* lda sp, 16(sp) */
144  0x27, 0x01, 0x1f, 0x20,	/* lda v0, 295(zero) */
145  0x83, 0x00, 0x00, 0x00,	/* call_pal callsys */
146};
147#define RETCODE_NWORDS		4
148#define RETCODE_SIZE		(RETCODE_NWORDS * 4)
149
150LONGEST
151alphanbsd_sigtramp_offset (CORE_ADDR pc)
152{
153  unsigned char ret[RETCODE_SIZE], w[4];
154  LONGEST off;
155  int i;
156
157  if (read_memory_nobpt (pc, (char *) w, 4) != 0)
158    return -1;
159
160  for (i = 0; i < RETCODE_NWORDS; i++)
161    {
162      if (memcmp (w, sigtramp_retcode + (i * 4), 4) == 0)
163	break;
164    }
165  if (i == RETCODE_NWORDS)
166    return (-1);
167
168  off = i * 4;
169  pc -= off;
170
171  if (read_memory_nobpt (pc, (char *) ret, sizeof (ret)) != 0)
172    return -1;
173
174  if (memcmp (ret, sigtramp_retcode, RETCODE_SIZE) == 0)
175    return off;
176
177  return -1;
178}
179
180static int
181alphanbsd_pc_in_sigtramp (CORE_ADDR pc, char *func_name)
182{
183  return (nbsd_pc_in_sigtramp (pc, func_name)
184	  || alphanbsd_sigtramp_offset (pc) >= 0);
185}
186
187static CORE_ADDR
188alphanbsd_sigcontext_addr (struct frame_info *frame)
189{
190  /* FIXME: This is not correct for all versions of NetBSD/alpha.
191     We will probably need to disassemble the trampoline to figure
192     out which trampoline frame type we have.  */
193  return get_frame_base (frame);
194}
195
196static void
197alphanbsd_init_abi (struct gdbarch_info info,
198                    struct gdbarch *gdbarch)
199{
200  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
201
202  /* Hook into the DWARF CFI frame unwinder.  */
203  alpha_dwarf2_init_abi (info, gdbarch);
204
205  /* Hook into the MDEBUG frame unwinder.  */
206  alpha_mdebug_init_abi (info, gdbarch);
207
208  set_gdbarch_pc_in_sigtramp (gdbarch, alphanbsd_pc_in_sigtramp);
209
210  /* NetBSD/alpha does not provide single step support via ptrace(2); we
211     must use software single-stepping.  */
212  set_gdbarch_software_single_step (gdbarch, alpha_software_single_step);
213
214  set_solib_svr4_fetch_link_map_offsets (gdbarch,
215                                 nbsd_lp64_solib_svr4_fetch_link_map_offsets);
216
217  tdep->dynamic_sigtramp_offset = alphanbsd_sigtramp_offset;
218  tdep->sigcontext_addr = alphanbsd_sigcontext_addr;
219
220  tdep->jb_pc = 2;
221  tdep->jb_elt_size = 8;
222}
223
224void
225_initialize_alphanbsd_tdep (void)
226{
227  gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_NETBSD_ELF,
228                          alphanbsd_init_abi);
229  gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_OPENBSD_ELF,
230                          alphanbsd_init_abi);
231
232  add_core_fns (&alphanbsd_core_fns);
233  add_core_fns (&alphanbsd_elfcore_fns);
234}
235