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