* NEWS: Note new MIPS NetBSD native configuration.
[deliverable/binutils-gdb.git] / gdb / mipsnbsd-tdep.c
CommitLineData
45888261
JT
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
36void
37mipsnbsd_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
53void
54mipsnbsd_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
63void
64mipsnbsd_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
80void
81mipsnbsd_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
90static void
91fetch_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
110static void
111fetch_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
136static 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
145static 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
173static 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
180static 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
187static LONGEST
188mipsnbsd_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
219static int
220mipsnbsd_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
237static int
238mipsnbsd_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
256static int
257mipsnbsd_cannot_fetch_register (int regno)
258{
259 return (regno >= FP_REGNUM
260 || regno == ZERO_REGNUM
261 || regno == FCRIR_REGNUM);
262}
263
264static int
265mipsnbsd_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. */
274static struct link_map_offsets *
275mipsnbsd_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
307static struct link_map_offsets *
308mipsnbsd_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
340static void
341mipsnbsd_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
359void
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}
This page took 0.044161 seconds and 4 git commands to generate.