1130803Smarcel/* Target-dependent code for MIPS systems running NetBSD.
2130803Smarcel   Copyright 2002, 2003 Free Software Foundation, Inc.
3130803Smarcel   Contributed by Wasabi Systems, Inc.
4130803Smarcel
5130803Smarcel   This file is part of GDB.
6130803Smarcel
7130803Smarcel   This program is free software; you can redistribute it and/or modify
8130803Smarcel   it under the terms of the GNU General Public License as published by
9130803Smarcel   the Free Software Foundation; either version 2 of the License, or
10130803Smarcel   (at your option) any later version.
11130803Smarcel
12130803Smarcel   This program is distributed in the hope that it will be useful,
13130803Smarcel   but WITHOUT ANY WARRANTY; without even the implied warranty of
14130803Smarcel   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15130803Smarcel   GNU General Public License for more details.
16130803Smarcel
17130803Smarcel   You should have received a copy of the GNU General Public License
18130803Smarcel   along with this program; if not, write to the Free Software
19130803Smarcel   Foundation, Inc., 59 Temple Place - Suite 330,
20130803Smarcel   Boston, MA 02111-1307, USA.  */
21130803Smarcel
22130803Smarcel#include "defs.h"
23130803Smarcel#include "gdbcore.h"
24130803Smarcel#include "regcache.h"
25130803Smarcel#include "target.h"
26130803Smarcel#include "value.h"
27130803Smarcel#include "osabi.h"
28130803Smarcel
29130803Smarcel#include "nbsd-tdep.h"
30130803Smarcel#include "mipsnbsd-tdep.h"
31130803Smarcel
32130803Smarcel#include "solib-svr4.h"
33130803Smarcel
34130803Smarcel/* Conveniently, GDB uses the same register numbering as the
35130803Smarcel   ptrace register structure used by NetBSD/mips.  */
36130803Smarcel
37130803Smarcelvoid
38130803Smarcelmipsnbsd_supply_reg (char *regs, int regno)
39130803Smarcel{
40130803Smarcel  int i;
41130803Smarcel
42130803Smarcel  for (i = 0; i <= PC_REGNUM; i++)
43130803Smarcel    {
44130803Smarcel      if (regno == i || regno == -1)
45130803Smarcel	{
46130803Smarcel	  if (CANNOT_FETCH_REGISTER (i))
47130803Smarcel	    supply_register (i, NULL);
48130803Smarcel	  else
49130803Smarcel            supply_register (i, regs + (i * mips_regsize (current_gdbarch)));
50130803Smarcel        }
51130803Smarcel    }
52130803Smarcel}
53130803Smarcel
54130803Smarcelvoid
55130803Smarcelmipsnbsd_fill_reg (char *regs, int regno)
56130803Smarcel{
57130803Smarcel  int i;
58130803Smarcel
59130803Smarcel  for (i = 0; i <= PC_REGNUM; i++)
60130803Smarcel    if ((regno == i || regno == -1) && ! CANNOT_STORE_REGISTER (i))
61130803Smarcel      regcache_collect (i, regs + (i * mips_regsize (current_gdbarch)));
62130803Smarcel}
63130803Smarcel
64130803Smarcelvoid
65130803Smarcelmipsnbsd_supply_fpreg (char *fpregs, int regno)
66130803Smarcel{
67130803Smarcel  int i;
68130803Smarcel
69130803Smarcel  for (i = FP0_REGNUM;
70130803Smarcel       i <= mips_regnum (current_gdbarch)->fp_implementation_revision;
71130803Smarcel       i++)
72130803Smarcel    {
73130803Smarcel      if (regno == i || regno == -1)
74130803Smarcel	{
75130803Smarcel	  if (CANNOT_FETCH_REGISTER (i))
76130803Smarcel	    supply_register (i, NULL);
77130803Smarcel	  else
78130803Smarcel            supply_register (i, fpregs + ((i - FP0_REGNUM) * mips_regsize (current_gdbarch)));
79130803Smarcel	}
80130803Smarcel    }
81130803Smarcel}
82130803Smarcel
83130803Smarcelvoid
84130803Smarcelmipsnbsd_fill_fpreg (char *fpregs, int regno)
85130803Smarcel{
86130803Smarcel  int i;
87130803Smarcel
88130803Smarcel  for (i = FP0_REGNUM; i <= mips_regnum (current_gdbarch)->fp_control_status;
89130803Smarcel       i++)
90130803Smarcel    if ((regno == i || regno == -1) && ! CANNOT_STORE_REGISTER (i))
91130803Smarcel      regcache_collect (i, fpregs + ((i - FP0_REGNUM) * mips_regsize (current_gdbarch)));
92130803Smarcel}
93130803Smarcel
94130803Smarcelstatic void
95130803Smarcelfetch_core_registers (char *core_reg_sect, unsigned core_reg_size, int which,
96130803Smarcel                      CORE_ADDR ignore)
97130803Smarcel{
98130803Smarcel  char *regs, *fpregs;
99130803Smarcel
100130803Smarcel  /* We get everything from one section.  */
101130803Smarcel  if (which != 0)
102130803Smarcel    return;
103130803Smarcel
104130803Smarcel  regs = core_reg_sect;
105130803Smarcel  fpregs = core_reg_sect + SIZEOF_STRUCT_REG;
106130803Smarcel
107130803Smarcel  /* Integer registers.  */
108130803Smarcel  mipsnbsd_supply_reg (regs, -1);
109130803Smarcel
110130803Smarcel  /* Floating point registers.  */
111130803Smarcel  mipsnbsd_supply_fpreg (fpregs, -1);
112130803Smarcel}
113130803Smarcel
114130803Smarcelstatic void
115130803Smarcelfetch_elfcore_registers (char *core_reg_sect, unsigned core_reg_size, int which,
116130803Smarcel                         CORE_ADDR ignore)
117130803Smarcel{
118130803Smarcel  switch (which)
119130803Smarcel    {
120130803Smarcel    case 0:  /* Integer registers.  */
121130803Smarcel      if (core_reg_size != SIZEOF_STRUCT_REG)
122130803Smarcel	warning ("Wrong size register set in core file.");
123130803Smarcel      else
124130803Smarcel	mipsnbsd_supply_reg (core_reg_sect, -1);
125130803Smarcel      break;
126130803Smarcel
127130803Smarcel    case 2:  /* Floating point registers.  */
128130803Smarcel      if (core_reg_size != SIZEOF_STRUCT_FPREG)
129130803Smarcel	warning ("Wrong size register set in core file.");
130130803Smarcel      else
131130803Smarcel	mipsnbsd_supply_fpreg (core_reg_sect, -1);
132130803Smarcel      break;
133130803Smarcel
134130803Smarcel    default:
135130803Smarcel      /* Don't know what kind of register request this is; just ignore it.  */
136130803Smarcel      break;
137130803Smarcel    }
138130803Smarcel}
139130803Smarcel
140130803Smarcelstatic struct core_fns mipsnbsd_core_fns =
141130803Smarcel{
142130803Smarcel  bfd_target_unknown_flavour,		/* core_flavour */
143130803Smarcel  default_check_format,			/* check_format */
144130803Smarcel  default_core_sniffer,			/* core_sniffer */
145130803Smarcel  fetch_core_registers,			/* core_read_registers */
146130803Smarcel  NULL					/* next */
147130803Smarcel};
148130803Smarcel
149130803Smarcelstatic struct core_fns mipsnbsd_elfcore_fns =
150130803Smarcel{
151130803Smarcel  bfd_target_elf_flavour,		/* core_flavour */
152130803Smarcel  default_check_format,			/* check_format */
153130803Smarcel  default_core_sniffer,			/* core_sniffer */
154130803Smarcel  fetch_elfcore_registers,		/* core_read_registers */
155130803Smarcel  NULL					/* next */
156130803Smarcel};
157130803Smarcel
158130803Smarcel/* Under NetBSD/mips, signal handler invocations can be identified by the
159130803Smarcel   designated code sequence that is used to return from a signal handler.
160130803Smarcel   In particular, the return address of a signal handler points to the
161130803Smarcel   following code sequence:
162130803Smarcel
163130803Smarcel	addu	a0, sp, 16
164130803Smarcel	li	v0, 295			# __sigreturn14
165130803Smarcel	syscall
166130803Smarcel
167130803Smarcel   Each instruction has a unique encoding, so we simply attempt to match
168130803Smarcel   the instruction the PC is pointing to with any of the above instructions.
169130803Smarcel   If there is a hit, we know the offset to the start of the designated
170130803Smarcel   sequence and can then check whether we really are executing in the
171130803Smarcel   signal trampoline.  If not, -1 is returned, otherwise the offset from the
172130803Smarcel   start of the return sequence is returned.  */
173130803Smarcel
174130803Smarcel#define RETCODE_NWORDS	3
175130803Smarcel#define RETCODE_SIZE	(RETCODE_NWORDS * 4)
176130803Smarcel
177130803Smarcelstatic const unsigned char sigtramp_retcode_mipsel[RETCODE_SIZE] =
178130803Smarcel{
179130803Smarcel  0x10, 0x00, 0xa4, 0x27,	/* addu a0, sp, 16 */
180130803Smarcel  0x27, 0x01, 0x02, 0x24,	/* li v0, 295 */
181130803Smarcel  0x0c, 0x00, 0x00, 0x00,	/* syscall */
182130803Smarcel};
183130803Smarcel
184130803Smarcelstatic const unsigned char sigtramp_retcode_mipseb[RETCODE_SIZE] =
185130803Smarcel{
186130803Smarcel  0x27, 0xa4, 0x00, 0x10,	/* addu a0, sp, 16 */
187130803Smarcel  0x24, 0x02, 0x01, 0x27,	/* li v0, 295 */
188130803Smarcel  0x00, 0x00, 0x00, 0x0c,	/* syscall */
189130803Smarcel};
190130803Smarcel
191130803Smarcelstatic LONGEST
192130803Smarcelmipsnbsd_sigtramp_offset (CORE_ADDR pc)
193130803Smarcel{
194130803Smarcel  const char *retcode = TARGET_BYTE_ORDER == BFD_ENDIAN_BIG
195130803Smarcel  	? sigtramp_retcode_mipseb : sigtramp_retcode_mipsel;
196130803Smarcel  unsigned char ret[RETCODE_SIZE], w[4];
197130803Smarcel  LONGEST off;
198130803Smarcel  int i;
199130803Smarcel
200130803Smarcel  if (read_memory_nobpt (pc, (char *) w, sizeof (w)) != 0)
201130803Smarcel    return -1;
202130803Smarcel
203130803Smarcel  for (i = 0; i < RETCODE_NWORDS; i++)
204130803Smarcel    {
205130803Smarcel      if (memcmp (w, retcode + (i * 4), 4) == 0)
206130803Smarcel	break;
207130803Smarcel    }
208130803Smarcel  if (i == RETCODE_NWORDS)
209130803Smarcel    return -1;
210130803Smarcel
211130803Smarcel  off = i * 4;
212130803Smarcel  pc -= off;
213130803Smarcel
214130803Smarcel  if (read_memory_nobpt (pc, (char *) ret, sizeof (ret)) != 0)
215130803Smarcel    return -1;
216130803Smarcel
217130803Smarcel  if (memcmp (ret, retcode, RETCODE_SIZE) == 0)
218130803Smarcel    return off;
219130803Smarcel
220130803Smarcel  return -1;
221130803Smarcel}
222130803Smarcel
223130803Smarcelstatic int
224130803Smarcelmipsnbsd_pc_in_sigtramp (CORE_ADDR pc, char *func_name)
225130803Smarcel{
226130803Smarcel  return (nbsd_pc_in_sigtramp (pc, func_name)
227130803Smarcel	  || mipsnbsd_sigtramp_offset (pc) >= 0);
228130803Smarcel}
229130803Smarcel
230130803Smarcel/* Figure out where the longjmp will land.  We expect that we have
231130803Smarcel   just entered longjmp and haven't yet setup the stack frame, so
232130803Smarcel   the args are still in the argument regs.  A0_REGNUM points at the
233130803Smarcel   jmp_buf structure from which we extract the PC that we will land
234130803Smarcel   at.  The PC is copied into *pc.  This routine returns true on
235130803Smarcel   success.  */
236130803Smarcel
237130803Smarcel#define NBSD_MIPS_JB_PC			(2 * 4)
238130803Smarcel#define NBSD_MIPS_JB_ELEMENT_SIZE	mips_regsize (current_gdbarch)
239130803Smarcel#define NBSD_MIPS_JB_OFFSET		(NBSD_MIPS_JB_PC * \
240130803Smarcel					 NBSD_MIPS_JB_ELEMENT_SIZE)
241130803Smarcel
242130803Smarcelstatic int
243130803Smarcelmipsnbsd_get_longjmp_target (CORE_ADDR *pc)
244130803Smarcel{
245130803Smarcel  CORE_ADDR jb_addr;
246130803Smarcel  char *buf;
247130803Smarcel
248130803Smarcel  buf = alloca (NBSD_MIPS_JB_ELEMENT_SIZE);
249130803Smarcel
250130803Smarcel  jb_addr = read_register (A0_REGNUM);
251130803Smarcel
252130803Smarcel  if (target_read_memory (jb_addr + NBSD_MIPS_JB_OFFSET, buf,
253130803Smarcel  			  NBSD_MIPS_JB_ELEMENT_SIZE))
254130803Smarcel    return 0;
255130803Smarcel
256130803Smarcel  *pc = extract_unsigned_integer (buf, NBSD_MIPS_JB_ELEMENT_SIZE);
257130803Smarcel
258130803Smarcel  return 1;
259130803Smarcel}
260130803Smarcel
261130803Smarcelstatic int
262130803Smarcelmipsnbsd_cannot_fetch_register (int regno)
263130803Smarcel{
264130803Smarcel  return (regno == ZERO_REGNUM
265130803Smarcel	  || regno == mips_regnum (current_gdbarch)->fp_implementation_revision);
266130803Smarcel}
267130803Smarcel
268130803Smarcelstatic int
269130803Smarcelmipsnbsd_cannot_store_register (int regno)
270130803Smarcel{
271130803Smarcel  return (regno == ZERO_REGNUM
272130803Smarcel	  || regno == mips_regnum (current_gdbarch)->fp_implementation_revision);
273130803Smarcel}
274130803Smarcel
275130803Smarcel/* NetBSD/mips uses a slightly different link_map structure from the
276130803Smarcel   other NetBSD platforms.  */
277130803Smarcelstatic struct link_map_offsets *
278130803Smarcelmipsnbsd_ilp32_solib_svr4_fetch_link_map_offsets (void)
279130803Smarcel{
280130803Smarcel  static struct link_map_offsets lmo;
281130803Smarcel  static struct link_map_offsets *lmp = NULL;
282130803Smarcel
283130803Smarcel  if (lmp == NULL)
284130803Smarcel    {
285130803Smarcel      lmp = &lmo;
286130803Smarcel
287130803Smarcel      lmo.r_debug_size = 16;
288130803Smarcel
289130803Smarcel      lmo.r_map_offset = 4;
290130803Smarcel      lmo.r_map_size   = 4;
291130803Smarcel
292130803Smarcel      lmo.link_map_size = 24;
293130803Smarcel
294130803Smarcel      lmo.l_addr_offset = 0;
295130803Smarcel      lmo.l_addr_size   = 4;
296130803Smarcel
297130803Smarcel      lmo.l_name_offset = 8;
298130803Smarcel      lmo.l_name_size   = 4;
299130803Smarcel
300130803Smarcel      lmo.l_next_offset = 16;
301130803Smarcel      lmo.l_next_size   = 4;
302130803Smarcel
303130803Smarcel      lmo.l_prev_offset = 20;
304130803Smarcel      lmo.l_prev_size   = 4;
305130803Smarcel    }
306130803Smarcel
307130803Smarcel  return lmp;
308130803Smarcel}
309130803Smarcel
310130803Smarcelstatic struct link_map_offsets *
311130803Smarcelmipsnbsd_lp64_solib_svr4_fetch_link_map_offsets (void)
312130803Smarcel{
313130803Smarcel  static struct link_map_offsets lmo;
314130803Smarcel  static struct link_map_offsets *lmp = NULL;
315130803Smarcel
316130803Smarcel  if (lmp == NULL)
317130803Smarcel    {
318130803Smarcel      lmp = &lmo;
319130803Smarcel
320130803Smarcel      lmo.r_debug_size = 32;
321130803Smarcel
322130803Smarcel      lmo.r_map_offset = 8;
323130803Smarcel      lmo.r_map_size   = 8;
324130803Smarcel
325130803Smarcel      lmo.link_map_size = 48;
326130803Smarcel
327130803Smarcel      lmo.l_addr_offset = 0;
328130803Smarcel      lmo.l_addr_size   = 8;
329130803Smarcel
330130803Smarcel      lmo.l_name_offset = 16;
331130803Smarcel      lmo.l_name_size   = 8;
332130803Smarcel
333130803Smarcel      lmo.l_next_offset = 32;
334130803Smarcel      lmo.l_next_size   = 8;
335130803Smarcel
336130803Smarcel      lmo.l_prev_offset = 40;
337130803Smarcel      lmo.l_prev_size   = 8;
338130803Smarcel    }
339130803Smarcel
340130803Smarcel  return lmp;
341130803Smarcel}
342130803Smarcel
343130803Smarcelstatic void
344130803Smarcelmipsnbsd_init_abi (struct gdbarch_info info,
345130803Smarcel                   struct gdbarch *gdbarch)
346130803Smarcel{
347130803Smarcel  set_gdbarch_pc_in_sigtramp (gdbarch, mipsnbsd_pc_in_sigtramp);
348130803Smarcel
349130803Smarcel  set_gdbarch_get_longjmp_target (gdbarch, mipsnbsd_get_longjmp_target);
350130803Smarcel
351130803Smarcel  set_gdbarch_cannot_fetch_register (gdbarch, mipsnbsd_cannot_fetch_register);
352130803Smarcel  set_gdbarch_cannot_store_register (gdbarch, mipsnbsd_cannot_store_register);
353130803Smarcel
354130803Smarcel  set_gdbarch_software_single_step (gdbarch, mips_software_single_step);
355130803Smarcel
356130803Smarcel  set_solib_svr4_fetch_link_map_offsets (gdbarch,
357130803Smarcel					 gdbarch_ptr_bit (gdbarch) == 32 ?
358130803Smarcel                            mipsnbsd_ilp32_solib_svr4_fetch_link_map_offsets :
359130803Smarcel			    mipsnbsd_lp64_solib_svr4_fetch_link_map_offsets);
360130803Smarcel}
361130803Smarcel
362130803Smarcelvoid
363130803Smarcel_initialize_mipsnbsd_tdep (void)
364130803Smarcel{
365130803Smarcel  gdbarch_register_osabi (bfd_arch_mips, 0, GDB_OSABI_NETBSD_ELF,
366130803Smarcel			  mipsnbsd_init_abi);
367130803Smarcel
368130803Smarcel  add_core_fns (&mipsnbsd_core_fns);
369130803Smarcel  add_core_fns (&mipsnbsd_elfcore_fns);
370130803Smarcel}
371