1/* Target-dependent code for MIPS systems running NetBSD.
2   Copyright 2002, 2003 Free Software Foundation, Inc.
3   Contributed by Wasabi Systems, 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 "gdbcore.h"
24#include "regcache.h"
25#include "target.h"
26#include "value.h"
27#include "osabi.h"
28
29#include "nbsd-tdep.h"
30#include "mipsnbsd-tdep.h"
31
32#include "solib-svr4.h"
33
34/* Conveniently, GDB uses the same register numbering as the
35   ptrace register structure used by NetBSD/mips.  */
36
37void
38mipsnbsd_supply_reg (char *regs, int regno)
39{
40  int i;
41
42  for (i = 0; i <= PC_REGNUM; i++)
43    {
44      if (regno == i || regno == -1)
45	{
46	  if (CANNOT_FETCH_REGISTER (i))
47	    supply_register (i, NULL);
48	  else
49            supply_register (i, regs + (i * mips_regsize (current_gdbarch)));
50        }
51    }
52}
53
54void
55mipsnbsd_fill_reg (char *regs, int regno)
56{
57  int i;
58
59  for (i = 0; i <= PC_REGNUM; i++)
60    if ((regno == i || regno == -1) && ! CANNOT_STORE_REGISTER (i))
61      regcache_collect (i, regs + (i * mips_regsize (current_gdbarch)));
62}
63
64void
65mipsnbsd_supply_fpreg (char *fpregs, int regno)
66{
67  int i;
68
69  for (i = FP0_REGNUM;
70       i <= mips_regnum (current_gdbarch)->fp_implementation_revision;
71       i++)
72    {
73      if (regno == i || regno == -1)
74	{
75	  if (CANNOT_FETCH_REGISTER (i))
76	    supply_register (i, NULL);
77	  else
78            supply_register (i, fpregs + ((i - FP0_REGNUM) * mips_regsize (current_gdbarch)));
79	}
80    }
81}
82
83void
84mipsnbsd_fill_fpreg (char *fpregs, int regno)
85{
86  int i;
87
88  for (i = FP0_REGNUM; i <= mips_regnum (current_gdbarch)->fp_control_status;
89       i++)
90    if ((regno == i || regno == -1) && ! CANNOT_STORE_REGISTER (i))
91      regcache_collect (i, fpregs + ((i - FP0_REGNUM) * mips_regsize (current_gdbarch)));
92}
93
94static void
95fetch_core_registers (char *core_reg_sect, unsigned core_reg_size, int which,
96                      CORE_ADDR ignore)
97{
98  char *regs, *fpregs;
99
100  /* We get everything from one section.  */
101  if (which != 0)
102    return;
103
104  regs = core_reg_sect;
105  fpregs = core_reg_sect + SIZEOF_STRUCT_REG;
106
107  /* Integer registers.  */
108  mipsnbsd_supply_reg (regs, -1);
109
110  /* Floating point registers.  */
111  mipsnbsd_supply_fpreg (fpregs, -1);
112}
113
114static void
115fetch_elfcore_registers (char *core_reg_sect, unsigned core_reg_size, int which,
116                         CORE_ADDR ignore)
117{
118  switch (which)
119    {
120    case 0:  /* Integer registers.  */
121      if (core_reg_size != SIZEOF_STRUCT_REG)
122	warning ("Wrong size register set in core file.");
123      else
124	mipsnbsd_supply_reg (core_reg_sect, -1);
125      break;
126
127    case 2:  /* Floating point registers.  */
128      if (core_reg_size != SIZEOF_STRUCT_FPREG)
129	warning ("Wrong size register set in core file.");
130      else
131	mipsnbsd_supply_fpreg (core_reg_sect, -1);
132      break;
133
134    default:
135      /* Don't know what kind of register request this is; just ignore it.  */
136      break;
137    }
138}
139
140static struct core_fns mipsnbsd_core_fns =
141{
142  bfd_target_unknown_flavour,		/* core_flavour */
143  default_check_format,			/* check_format */
144  default_core_sniffer,			/* core_sniffer */
145  fetch_core_registers,			/* core_read_registers */
146  NULL					/* next */
147};
148
149static struct core_fns mipsnbsd_elfcore_fns =
150{
151  bfd_target_elf_flavour,		/* core_flavour */
152  default_check_format,			/* check_format */
153  default_core_sniffer,			/* core_sniffer */
154  fetch_elfcore_registers,		/* core_read_registers */
155  NULL					/* next */
156};
157
158/* Under NetBSD/mips, signal handler invocations can be identified by the
159   designated code sequence that is used to return from a signal handler.
160   In particular, the return address of a signal handler points to the
161   following code sequence:
162
163	addu	a0, sp, 16
164	li	v0, 295			# __sigreturn14
165	syscall
166
167   Each instruction has a unique encoding, so we simply attempt to match
168   the instruction the PC is pointing to with any of the above instructions.
169   If there is a hit, we know the offset to the start of the designated
170   sequence and can then check whether we really are executing in the
171   signal trampoline.  If not, -1 is returned, otherwise the offset from the
172   start of the return sequence is returned.  */
173
174#define RETCODE_NWORDS	3
175#define RETCODE_SIZE	(RETCODE_NWORDS * 4)
176
177static const unsigned char sigtramp_retcode_mipsel[RETCODE_SIZE] =
178{
179  0x10, 0x00, 0xa4, 0x27,	/* addu a0, sp, 16 */
180  0x27, 0x01, 0x02, 0x24,	/* li v0, 295 */
181  0x0c, 0x00, 0x00, 0x00,	/* syscall */
182};
183
184static const unsigned char sigtramp_retcode_mipseb[RETCODE_SIZE] =
185{
186  0x27, 0xa4, 0x00, 0x10,	/* addu a0, sp, 16 */
187  0x24, 0x02, 0x01, 0x27,	/* li v0, 295 */
188  0x00, 0x00, 0x00, 0x0c,	/* syscall */
189};
190
191static LONGEST
192mipsnbsd_sigtramp_offset (CORE_ADDR pc)
193{
194  const char *retcode = TARGET_BYTE_ORDER == BFD_ENDIAN_BIG
195  	? sigtramp_retcode_mipseb : sigtramp_retcode_mipsel;
196  unsigned char ret[RETCODE_SIZE], w[4];
197  LONGEST off;
198  int i;
199
200  if (read_memory_nobpt (pc, (char *) w, sizeof (w)) != 0)
201    return -1;
202
203  for (i = 0; i < RETCODE_NWORDS; i++)
204    {
205      if (memcmp (w, retcode + (i * 4), 4) == 0)
206	break;
207    }
208  if (i == RETCODE_NWORDS)
209    return -1;
210
211  off = i * 4;
212  pc -= off;
213
214  if (read_memory_nobpt (pc, (char *) ret, sizeof (ret)) != 0)
215    return -1;
216
217  if (memcmp (ret, retcode, RETCODE_SIZE) == 0)
218    return off;
219
220  return -1;
221}
222
223static int
224mipsnbsd_pc_in_sigtramp (CORE_ADDR pc, char *func_name)
225{
226  return (nbsd_pc_in_sigtramp (pc, func_name)
227	  || mipsnbsd_sigtramp_offset (pc) >= 0);
228}
229
230/* Figure out where the longjmp will land.  We expect that we have
231   just entered longjmp and haven't yet setup the stack frame, so
232   the args are still in the argument regs.  A0_REGNUM points at the
233   jmp_buf structure from which we extract the PC that we will land
234   at.  The PC is copied into *pc.  This routine returns true on
235   success.  */
236
237#define NBSD_MIPS_JB_PC			(2 * 4)
238#define NBSD_MIPS_JB_ELEMENT_SIZE	mips_regsize (current_gdbarch)
239#define NBSD_MIPS_JB_OFFSET		(NBSD_MIPS_JB_PC * \
240					 NBSD_MIPS_JB_ELEMENT_SIZE)
241
242static int
243mipsnbsd_get_longjmp_target (CORE_ADDR *pc)
244{
245  CORE_ADDR jb_addr;
246  char *buf;
247
248  buf = alloca (NBSD_MIPS_JB_ELEMENT_SIZE);
249
250  jb_addr = read_register (A0_REGNUM);
251
252  if (target_read_memory (jb_addr + NBSD_MIPS_JB_OFFSET, buf,
253  			  NBSD_MIPS_JB_ELEMENT_SIZE))
254    return 0;
255
256  *pc = extract_unsigned_integer (buf, NBSD_MIPS_JB_ELEMENT_SIZE);
257
258  return 1;
259}
260
261static int
262mipsnbsd_cannot_fetch_register (int regno)
263{
264  return (regno == ZERO_REGNUM
265	  || regno == mips_regnum (current_gdbarch)->fp_implementation_revision);
266}
267
268static int
269mipsnbsd_cannot_store_register (int regno)
270{
271  return (regno == ZERO_REGNUM
272	  || regno == mips_regnum (current_gdbarch)->fp_implementation_revision);
273}
274
275/* NetBSD/mips uses a slightly different link_map structure from the
276   other NetBSD platforms.  */
277static struct link_map_offsets *
278mipsnbsd_ilp32_solib_svr4_fetch_link_map_offsets (void)
279{
280  static struct link_map_offsets lmo;
281  static struct link_map_offsets *lmp = NULL;
282
283  if (lmp == NULL)
284    {
285      lmp = &lmo;
286
287      lmo.r_debug_size = 16;
288
289      lmo.r_map_offset = 4;
290      lmo.r_map_size   = 4;
291
292      lmo.link_map_size = 24;
293
294      lmo.l_addr_offset = 0;
295      lmo.l_addr_size   = 4;
296
297      lmo.l_name_offset = 8;
298      lmo.l_name_size   = 4;
299
300      lmo.l_next_offset = 16;
301      lmo.l_next_size   = 4;
302
303      lmo.l_prev_offset = 20;
304      lmo.l_prev_size   = 4;
305    }
306
307  return lmp;
308}
309
310static struct link_map_offsets *
311mipsnbsd_lp64_solib_svr4_fetch_link_map_offsets (void)
312{
313  static struct link_map_offsets lmo;
314  static struct link_map_offsets *lmp = NULL;
315
316  if (lmp == NULL)
317    {
318      lmp = &lmo;
319
320      lmo.r_debug_size = 32;
321
322      lmo.r_map_offset = 8;
323      lmo.r_map_size   = 8;
324
325      lmo.link_map_size = 48;
326
327      lmo.l_addr_offset = 0;
328      lmo.l_addr_size   = 8;
329
330      lmo.l_name_offset = 16;
331      lmo.l_name_size   = 8;
332
333      lmo.l_next_offset = 32;
334      lmo.l_next_size   = 8;
335
336      lmo.l_prev_offset = 40;
337      lmo.l_prev_size   = 8;
338    }
339
340  return lmp;
341}
342
343static void
344mipsnbsd_init_abi (struct gdbarch_info info,
345                   struct gdbarch *gdbarch)
346{
347  set_gdbarch_pc_in_sigtramp (gdbarch, mipsnbsd_pc_in_sigtramp);
348
349  set_gdbarch_get_longjmp_target (gdbarch, mipsnbsd_get_longjmp_target);
350
351  set_gdbarch_cannot_fetch_register (gdbarch, mipsnbsd_cannot_fetch_register);
352  set_gdbarch_cannot_store_register (gdbarch, mipsnbsd_cannot_store_register);
353
354  set_gdbarch_software_single_step (gdbarch, mips_software_single_step);
355
356  set_solib_svr4_fetch_link_map_offsets (gdbarch,
357					 gdbarch_ptr_bit (gdbarch) == 32 ?
358                            mipsnbsd_ilp32_solib_svr4_fetch_link_map_offsets :
359			    mipsnbsd_lp64_solib_svr4_fetch_link_map_offsets);
360}
361
362void
363_initialize_mipsnbsd_tdep (void)
364{
365  gdbarch_register_osabi (bfd_arch_mips, 0, GDB_OSABI_NETBSD_ELF,
366			  mipsnbsd_init_abi);
367
368  add_core_fns (&mipsnbsd_core_fns);
369  add_core_fns (&mipsnbsd_elfcore_fns);
370}
371