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