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