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