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