Commit | Line | Data |
---|---|---|
d1180b0f MK |
1 | /* Target-dependent code for NetBSD/mips. |
2 | ||
1777c7b4 | 3 | Copyright 2002, 2003, 2004 Free Software Foundation, Inc. |
45888261 JT |
4 | Contributed by Wasabi Systems, Inc. |
5 | ||
6 | This file is part of GDB. | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
20 | Foundation, Inc., 59 Temple Place - Suite 330, | |
21 | Boston, MA 02111-1307, USA. */ | |
22 | ||
23 | #include "defs.h" | |
24 | #include "gdbcore.h" | |
25 | #include "regcache.h" | |
d1180b0f | 26 | #include "regset.h" |
45888261 JT |
27 | #include "target.h" |
28 | #include "value.h" | |
29 | #include "osabi.h" | |
30 | ||
d1180b0f | 31 | #include "gdb_assert.h" |
4c7d22cb MK |
32 | #include "gdb_string.h" |
33 | ||
3d9b49b0 | 34 | #include "nbsd-tdep.h" |
45888261 | 35 | #include "mipsnbsd-tdep.h" |
1777c7b4 | 36 | #include "mips-tdep.h" |
45888261 JT |
37 | |
38 | #include "solib-svr4.h" | |
39 | ||
d1180b0f MK |
40 | /* Shorthand for some register numbers used below. */ |
41 | #define MIPS_PC_REGNUM MIPS_EMBED_PC_REGNUM | |
42 | #define MIPS_FP0_REGNUM MIPS_EMBED_FP0_REGNUM | |
43 | #define MIPS_FSR_REGNUM MIPS_EMBED_FP0_REGNUM + 32 | |
44 | ||
45 | /* Core file support. */ | |
46 | ||
47 | /* Number of registers in `struct reg' from <machine/reg.h>. */ | |
48 | #define MIPSNBSD_NUM_GREGS 38 | |
49 | ||
50 | /* Number of registers in `struct fpreg' from <machine/reg.h>. */ | |
51 | #define MIPSNBSD_NUM_FPREGS 33 | |
52 | ||
53 | /* Supply register REGNUM from the buffer specified by FPREGS and LEN | |
54 | in the floating-point register set REGSET to register cache | |
55 | REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */ | |
56 | ||
57 | static void | |
58 | mipsnbsd_supply_fpregset (const struct regset *regset, | |
59 | struct regcache *regcache, | |
60 | int regnum, const void *fpregs, size_t len) | |
61 | { | |
62 | size_t regsize = mips_isa_regsize (get_regcache_arch (regcache)); | |
63 | const char *regs = fpregs; | |
64 | int i; | |
65 | ||
66 | gdb_assert (len >= MIPSNBSD_NUM_FPREGS * regsize); | |
67 | ||
68 | for (i = MIPS_FP0_REGNUM; i <= MIPS_FSR_REGNUM; i++) | |
69 | { | |
70 | if (regnum == i || regnum == -1) | |
71 | regcache_raw_supply (regcache, i, | |
72 | regs + (i - MIPS_FP0_REGNUM) * regsize); | |
73 | } | |
74 | } | |
75 | ||
76 | /* Supply register REGNUM from the buffer specified by GREGS and LEN | |
77 | in the general-purpose register set REGSET to register cache | |
78 | REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */ | |
79 | ||
80 | static void | |
81 | mipsnbsd_supply_gregset (const struct regset *regset, | |
82 | struct regcache *regcache, int regnum, | |
83 | const void *gregs, size_t len) | |
84 | { | |
85 | size_t regsize = mips_isa_regsize (get_regcache_arch (regcache)); | |
86 | const char *regs = gregs; | |
87 | int i; | |
88 | ||
89 | gdb_assert (len >= MIPSNBSD_NUM_GREGS * regsize); | |
90 | ||
91 | for (i = 0; i <= MIPS_PC_REGNUM; i++) | |
92 | { | |
93 | if (regnum == i || regnum == -1) | |
94 | regcache_raw_supply (regcache, i, regs + i * regsize); | |
95 | } | |
96 | ||
97 | if (len >= (MIPSNBSD_NUM_GREGS + MIPSNBSD_NUM_FPREGS) * regsize) | |
98 | { | |
99 | regs += MIPSNBSD_NUM_GREGS * regsize; | |
100 | len -= MIPSNBSD_NUM_GREGS * regsize; | |
101 | mipsnbsd_supply_fpregset (regset, regcache, regnum, regs, len); | |
102 | } | |
103 | } | |
104 | ||
105 | /* NetBSD/mips register sets. */ | |
106 | ||
107 | static struct regset mipsnbsd_gregset = | |
108 | { | |
109 | NULL, | |
110 | mipsnbsd_supply_gregset | |
111 | }; | |
112 | ||
113 | static struct regset mipsnbsd_fpregset = | |
114 | { | |
115 | NULL, | |
116 | mipsnbsd_supply_fpregset | |
117 | }; | |
118 | ||
119 | /* Return the appropriate register set for the core section identified | |
120 | by SECT_NAME and SECT_SIZE. */ | |
121 | ||
122 | static const struct regset * | |
123 | mipsnbsd_regset_from_core_section (struct gdbarch *gdbarch, | |
124 | const char *sect_name, size_t sect_size) | |
125 | { | |
126 | size_t regsize = mips_isa_regsize (gdbarch); | |
127 | ||
128 | if (strcmp (sect_name, ".reg") == 0 | |
129 | && sect_size >= MIPSNBSD_NUM_GREGS * regsize) | |
130 | return &mipsnbsd_gregset; | |
131 | ||
132 | if (strcmp (sect_name, ".reg2") == 0 | |
133 | && sect_size >= MIPSNBSD_NUM_FPREGS * regsize) | |
134 | return &mipsnbsd_fpregset; | |
135 | ||
136 | return NULL; | |
137 | } | |
138 | \f | |
139 | ||
45888261 JT |
140 | /* Conveniently, GDB uses the same register numbering as the |
141 | ptrace register structure used by NetBSD/mips. */ | |
142 | ||
143 | void | |
144 | mipsnbsd_supply_reg (char *regs, int regno) | |
145 | { | |
146 | int i; | |
147 | ||
148 | for (i = 0; i <= PC_REGNUM; i++) | |
149 | { | |
150 | if (regno == i || regno == -1) | |
151 | { | |
152 | if (CANNOT_FETCH_REGISTER (i)) | |
23a6d369 | 153 | regcache_raw_supply (current_regcache, i, NULL); |
45888261 | 154 | else |
23a6d369 AC |
155 | regcache_raw_supply (current_regcache, i, |
156 | regs + (i * mips_isa_regsize (current_gdbarch))); | |
45888261 JT |
157 | } |
158 | } | |
159 | } | |
160 | ||
161 | void | |
162 | mipsnbsd_fill_reg (char *regs, int regno) | |
163 | { | |
164 | int i; | |
165 | ||
166 | for (i = 0; i <= PC_REGNUM; i++) | |
167 | if ((regno == i || regno == -1) && ! CANNOT_STORE_REGISTER (i)) | |
822c9732 AC |
168 | regcache_raw_collect (current_regcache, i, |
169 | regs + (i * mips_isa_regsize (current_gdbarch))); | |
45888261 JT |
170 | } |
171 | ||
172 | void | |
173 | mipsnbsd_supply_fpreg (char *fpregs, int regno) | |
174 | { | |
175 | int i; | |
176 | ||
56cea623 AC |
177 | for (i = FP0_REGNUM; |
178 | i <= mips_regnum (current_gdbarch)->fp_implementation_revision; | |
179 | i++) | |
45888261 JT |
180 | { |
181 | if (regno == i || regno == -1) | |
182 | { | |
183 | if (CANNOT_FETCH_REGISTER (i)) | |
23a6d369 | 184 | regcache_raw_supply (current_regcache, i, NULL); |
45888261 | 185 | else |
23a6d369 AC |
186 | regcache_raw_supply (current_regcache, i, |
187 | fpregs + ((i - FP0_REGNUM) * mips_isa_regsize (current_gdbarch))); | |
45888261 JT |
188 | } |
189 | } | |
190 | } | |
191 | ||
192 | void | |
193 | mipsnbsd_fill_fpreg (char *fpregs, int regno) | |
194 | { | |
195 | int i; | |
196 | ||
56cea623 AC |
197 | for (i = FP0_REGNUM; i <= mips_regnum (current_gdbarch)->fp_control_status; |
198 | i++) | |
45888261 | 199 | if ((regno == i || regno == -1) && ! CANNOT_STORE_REGISTER (i)) |
822c9732 AC |
200 | regcache_raw_collect (current_regcache, i, |
201 | fpregs + ((i - FP0_REGNUM) * mips_isa_regsize (current_gdbarch))); | |
45888261 JT |
202 | } |
203 | ||
45888261 JT |
204 | /* Under NetBSD/mips, signal handler invocations can be identified by the |
205 | designated code sequence that is used to return from a signal handler. | |
206 | In particular, the return address of a signal handler points to the | |
207 | following code sequence: | |
208 | ||
209 | addu a0, sp, 16 | |
210 | li v0, 295 # __sigreturn14 | |
211 | syscall | |
212 | ||
213 | Each instruction has a unique encoding, so we simply attempt to match | |
214 | the instruction the PC is pointing to with any of the above instructions. | |
215 | If there is a hit, we know the offset to the start of the designated | |
216 | sequence and can then check whether we really are executing in the | |
217 | signal trampoline. If not, -1 is returned, otherwise the offset from the | |
218 | start of the return sequence is returned. */ | |
219 | ||
220 | #define RETCODE_NWORDS 3 | |
221 | #define RETCODE_SIZE (RETCODE_NWORDS * 4) | |
222 | ||
223 | static const unsigned char sigtramp_retcode_mipsel[RETCODE_SIZE] = | |
224 | { | |
225 | 0x10, 0x00, 0xa4, 0x27, /* addu a0, sp, 16 */ | |
226 | 0x27, 0x01, 0x02, 0x24, /* li v0, 295 */ | |
227 | 0x0c, 0x00, 0x00, 0x00, /* syscall */ | |
228 | }; | |
229 | ||
230 | static const unsigned char sigtramp_retcode_mipseb[RETCODE_SIZE] = | |
231 | { | |
232 | 0x27, 0xa4, 0x00, 0x10, /* addu a0, sp, 16 */ | |
233 | 0x24, 0x02, 0x01, 0x27, /* li v0, 295 */ | |
234 | 0x00, 0x00, 0x00, 0x0c, /* syscall */ | |
235 | }; | |
236 | ||
237 | static LONGEST | |
4c7d22cb | 238 | mipsnbsd_sigtramp_offset (struct frame_info *next_frame) |
45888261 | 239 | { |
4c7d22cb | 240 | CORE_ADDR pc = frame_pc_unwind (next_frame); |
45888261 JT |
241 | const char *retcode = TARGET_BYTE_ORDER == BFD_ENDIAN_BIG |
242 | ? sigtramp_retcode_mipseb : sigtramp_retcode_mipsel; | |
243 | unsigned char ret[RETCODE_SIZE], w[4]; | |
244 | LONGEST off; | |
245 | int i; | |
246 | ||
4c7d22cb | 247 | if (!safe_frame_unwind_memory (next_frame, pc, w, sizeof (w))) |
45888261 JT |
248 | return -1; |
249 | ||
250 | for (i = 0; i < RETCODE_NWORDS; i++) | |
251 | { | |
252 | if (memcmp (w, retcode + (i * 4), 4) == 0) | |
253 | break; | |
254 | } | |
255 | if (i == RETCODE_NWORDS) | |
256 | return -1; | |
257 | ||
258 | off = i * 4; | |
259 | pc -= off; | |
260 | ||
4c7d22cb | 261 | if (!safe_frame_unwind_memory (next_frame, pc, ret, sizeof (ret))) |
45888261 JT |
262 | return -1; |
263 | ||
264 | if (memcmp (ret, retcode, RETCODE_SIZE) == 0) | |
265 | return off; | |
266 | ||
267 | return -1; | |
268 | } | |
269 | ||
45888261 | 270 | /* Figure out where the longjmp will land. We expect that we have |
4c7d22cb MK |
271 | just entered longjmp and haven't yet setup the stack frame, so the |
272 | args are still in the argument regs. MIPS_A0_REGNUM points at the | |
45888261 JT |
273 | jmp_buf structure from which we extract the PC that we will land |
274 | at. The PC is copied into *pc. This routine returns true on | |
275 | success. */ | |
276 | ||
277 | #define NBSD_MIPS_JB_PC (2 * 4) | |
1b13c4f6 | 278 | #define NBSD_MIPS_JB_ELEMENT_SIZE mips_isa_regsize (current_gdbarch) |
45888261 JT |
279 | #define NBSD_MIPS_JB_OFFSET (NBSD_MIPS_JB_PC * \ |
280 | NBSD_MIPS_JB_ELEMENT_SIZE) | |
281 | ||
282 | static int | |
283 | mipsnbsd_get_longjmp_target (CORE_ADDR *pc) | |
284 | { | |
285 | CORE_ADDR jb_addr; | |
286 | char *buf; | |
287 | ||
288 | buf = alloca (NBSD_MIPS_JB_ELEMENT_SIZE); | |
289 | ||
4c7d22cb | 290 | jb_addr = read_register (MIPS_A0_REGNUM); |
45888261 JT |
291 | |
292 | if (target_read_memory (jb_addr + NBSD_MIPS_JB_OFFSET, buf, | |
293 | NBSD_MIPS_JB_ELEMENT_SIZE)) | |
294 | return 0; | |
295 | ||
7c0b4a20 | 296 | *pc = extract_unsigned_integer (buf, NBSD_MIPS_JB_ELEMENT_SIZE); |
45888261 JT |
297 | |
298 | return 1; | |
299 | } | |
300 | ||
301 | static int | |
302 | mipsnbsd_cannot_fetch_register (int regno) | |
303 | { | |
4c7d22cb | 304 | return (regno == MIPS_ZERO_REGNUM |
56cea623 | 305 | || regno == mips_regnum (current_gdbarch)->fp_implementation_revision); |
45888261 JT |
306 | } |
307 | ||
308 | static int | |
309 | mipsnbsd_cannot_store_register (int regno) | |
310 | { | |
4c7d22cb | 311 | return (regno == MIPS_ZERO_REGNUM |
56cea623 | 312 | || regno == mips_regnum (current_gdbarch)->fp_implementation_revision); |
45888261 JT |
313 | } |
314 | ||
fabe86c8 MK |
315 | /* Shared library support. */ |
316 | ||
317 | /* NetBSD/mips uses a slightly different `struct link_map' than the | |
45888261 | 318 | other NetBSD platforms. */ |
fabe86c8 | 319 | |
45888261 | 320 | static struct link_map_offsets * |
fabe86c8 | 321 | mipsnbsd_ilp32_fetch_link_map_offsets (void) |
45888261 JT |
322 | { |
323 | static struct link_map_offsets lmo; | |
324 | static struct link_map_offsets *lmp = NULL; | |
325 | ||
326 | if (lmp == NULL) | |
327 | { | |
328 | lmp = &lmo; | |
329 | ||
fabe86c8 MK |
330 | /* Everything we need is in the first 8 bytes. */ |
331 | lmo.r_debug_size = 8; | |
45888261 JT |
332 | lmo.r_map_offset = 4; |
333 | lmo.r_map_size = 4; | |
334 | ||
fabe86c8 | 335 | /* Everything we need is in the first 24 bytes. */ |
45888261 | 336 | lmo.link_map_size = 24; |
4c7d22cb | 337 | lmo.l_addr_offset = 4; |
45888261 | 338 | lmo.l_addr_size = 4; |
45888261 JT |
339 | lmo.l_name_offset = 8; |
340 | lmo.l_name_size = 4; | |
45888261 JT |
341 | lmo.l_next_offset = 16; |
342 | lmo.l_next_size = 4; | |
45888261 JT |
343 | lmo.l_prev_offset = 20; |
344 | lmo.l_prev_size = 4; | |
345 | } | |
346 | ||
347 | return lmp; | |
348 | } | |
349 | ||
350 | static struct link_map_offsets * | |
fabe86c8 | 351 | mipsnbsd_lp64_fetch_link_map_offsets (void) |
45888261 JT |
352 | { |
353 | static struct link_map_offsets lmo; | |
354 | static struct link_map_offsets *lmp = NULL; | |
355 | ||
356 | if (lmp == NULL) | |
357 | { | |
358 | lmp = &lmo; | |
359 | ||
fabe86c8 MK |
360 | /* Everything we need is in the first 16 bytes. */ |
361 | lmo.r_debug_size = 16; | |
45888261 JT |
362 | lmo.r_map_offset = 8; |
363 | lmo.r_map_size = 8; | |
364 | ||
fabe86c8 | 365 | /* Everything we need is in the first 40 bytes. */ |
45888261 | 366 | lmo.link_map_size = 48; |
45888261 JT |
367 | lmo.l_addr_offset = 0; |
368 | lmo.l_addr_size = 8; | |
45888261 JT |
369 | lmo.l_name_offset = 16; |
370 | lmo.l_name_size = 8; | |
45888261 JT |
371 | lmo.l_next_offset = 32; |
372 | lmo.l_next_size = 8; | |
45888261 JT |
373 | lmo.l_prev_offset = 40; |
374 | lmo.l_prev_size = 8; | |
375 | } | |
376 | ||
377 | return lmp; | |
378 | } | |
fabe86c8 | 379 | \f |
45888261 JT |
380 | |
381 | static void | |
382 | mipsnbsd_init_abi (struct gdbarch_info info, | |
383 | struct gdbarch *gdbarch) | |
384 | { | |
d1180b0f MK |
385 | set_gdbarch_regset_from_core_section |
386 | (gdbarch, mipsnbsd_regset_from_core_section); | |
387 | ||
45888261 JT |
388 | set_gdbarch_get_longjmp_target (gdbarch, mipsnbsd_get_longjmp_target); |
389 | ||
390 | set_gdbarch_cannot_fetch_register (gdbarch, mipsnbsd_cannot_fetch_register); | |
391 | set_gdbarch_cannot_store_register (gdbarch, mipsnbsd_cannot_store_register); | |
392 | ||
393 | set_gdbarch_software_single_step (gdbarch, mips_software_single_step); | |
394 | ||
fabe86c8 MK |
395 | /* NetBSD/mips has SVR4-style shared libraries. */ |
396 | set_solib_svr4_fetch_link_map_offsets | |
397 | (gdbarch, (gdbarch_ptr_bit (gdbarch) == 32 ? | |
398 | mipsnbsd_ilp32_fetch_link_map_offsets : | |
399 | mipsnbsd_lp64_fetch_link_map_offsets)); | |
45888261 | 400 | } |
d1180b0f MK |
401 | \f |
402 | ||
403 | static enum gdb_osabi | |
404 | mipsnbsd_core_osabi_sniffer (bfd *abfd) | |
405 | { | |
406 | if (strcmp (bfd_get_target (abfd), "netbsd-core") == 0) | |
407 | return GDB_OSABI_NETBSD_ELF; | |
408 | ||
409 | return GDB_OSABI_UNKNOWN; | |
410 | } | |
45888261 JT |
411 | |
412 | void | |
413 | _initialize_mipsnbsd_tdep (void) | |
414 | { | |
05816f70 | 415 | gdbarch_register_osabi (bfd_arch_mips, 0, GDB_OSABI_NETBSD_ELF, |
45888261 | 416 | mipsnbsd_init_abi); |
45888261 | 417 | } |