2011-05-23 Pedro Alves <pedro@codesourcery.com>
[deliverable/binutils-gdb.git] / gdb / alphanbsd-tdep.c
1 /* Target-dependent code for NetBSD/alpha.
2
3 Copyright (C) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011
4 Free Software Foundation, Inc.
5
6 Contributed by Wasabi Systems, Inc.
7
8 This file is part of GDB.
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22
23 #include "defs.h"
24 #include "frame.h"
25 #include "gdbcore.h"
26 #include "osabi.h"
27 #include "regcache.h"
28 #include "regset.h"
29 #include "value.h"
30
31 #include "gdb_assert.h"
32 #include "gdb_string.h"
33
34 #include "alpha-tdep.h"
35 #include "alphabsd-tdep.h"
36 #include "nbsd-tdep.h"
37 #include "solib-svr4.h"
38 #include "target.h"
39
40 /* Core file support. */
41
42 /* Even though NetBSD/alpha used ELF since day one, it used the
43 traditional a.out-style core dump format before NetBSD 1.6. */
44
45 /* Sizeof `struct reg' in <machine/reg.h>. */
46 #define ALPHANBSD_SIZEOF_GREGS (32 * 8)
47
48 /* Sizeof `struct fpreg' in <machine/reg.h. */
49 #define ALPHANBSD_SIZEOF_FPREGS ((32 * 8) + 8)
50
51 /* Supply register REGNUM from the buffer specified by FPREGS and LEN
52 in the floating-point register set REGSET to register cache
53 REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */
54
55 static void
56 alphanbsd_supply_fpregset (const struct regset *regset,
57 struct regcache *regcache,
58 int regnum, const void *fpregs, size_t len)
59 {
60 const gdb_byte *regs = fpregs;
61 int i;
62
63 gdb_assert (len >= ALPHANBSD_SIZEOF_FPREGS);
64
65 for (i = ALPHA_FP0_REGNUM; i < ALPHA_FP0_REGNUM + 31; i++)
66 {
67 if (regnum == i || regnum == -1)
68 regcache_raw_supply (regcache, i, regs + (i - ALPHA_FP0_REGNUM) * 8);
69 }
70
71 if (regnum == ALPHA_FPCR_REGNUM || regnum == -1)
72 regcache_raw_supply (regcache, ALPHA_FPCR_REGNUM, regs + 32 * 8);
73 }
74
75 /* Supply register REGNUM from the buffer specified by GREGS and LEN
76 in the general-purpose register set REGSET to register cache
77 REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */
78
79 static void
80 alphanbsd_supply_gregset (const struct regset *regset,
81 struct regcache *regcache,
82 int regnum, const void *gregs, size_t len)
83 {
84 const gdb_byte *regs = gregs;
85 int i;
86
87 gdb_assert (len >= ALPHANBSD_SIZEOF_GREGS);
88
89 for (i = 0; i < ALPHA_ZERO_REGNUM; i++)
90 {
91 if (regnum == i || regnum == -1)
92 regcache_raw_supply (regcache, i, regs + i * 8);
93 }
94
95 if (regnum == ALPHA_PC_REGNUM || regnum == -1)
96 regcache_raw_supply (regcache, ALPHA_PC_REGNUM, regs + 31 * 8);
97 }
98
99 /* Supply register REGNUM from the buffer specified by GREGS and LEN
100 in the general-purpose register set REGSET to register cache
101 REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */
102
103 static void
104 alphanbsd_aout_supply_gregset (const struct regset *regset,
105 struct regcache *regcache,
106 int regnum, const void *gregs, size_t len)
107 {
108 const gdb_byte *regs = gregs;
109 int i;
110
111 /* Table to map a GDB register number to a trapframe register index. */
112 static const int regmap[] =
113 {
114 0, 1, 2, 3,
115 4, 5, 6, 7,
116 8, 9, 10, 11,
117 12, 13, 14, 15,
118 30, 31, 32, 16,
119 17, 18, 19, 20,
120 21, 22, 23, 24,
121 25, 29, 26
122 };
123
124 gdb_assert (len >= ALPHANBSD_SIZEOF_GREGS);
125
126 for (i = 0; i < ARRAY_SIZE(regmap); i++)
127 {
128 if (regnum == i || regnum == -1)
129 regcache_raw_supply (regcache, i, regs + regmap[i] * 8);
130 }
131
132 if (regnum == ALPHA_PC_REGNUM || regnum == -1)
133 regcache_raw_supply (regcache, ALPHA_PC_REGNUM, regs + 31 * 8);
134
135 if (len >= ALPHANBSD_SIZEOF_GREGS + ALPHANBSD_SIZEOF_FPREGS)
136 {
137 regs += ALPHANBSD_SIZEOF_GREGS;
138 len -= ALPHANBSD_SIZEOF_GREGS;
139 alphanbsd_supply_fpregset (regset, regcache, regnum, regs, len);
140 }
141 }
142
143 /* NetBSD/alpha register sets. */
144
145 static struct regset alphanbsd_gregset =
146 {
147 NULL,
148 alphanbsd_supply_gregset
149 };
150
151 static struct regset alphanbsd_fpregset =
152 {
153 NULL,
154 alphanbsd_supply_fpregset
155 };
156
157 static struct regset alphanbsd_aout_gregset =
158 {
159 NULL,
160 alphanbsd_aout_supply_gregset
161 };
162
163 /* Return the appropriate register set for the core section identified
164 by SECT_NAME and SECT_SIZE. */
165
166 const struct regset *
167 alphanbsd_regset_from_core_section (struct gdbarch *gdbarch,
168 const char *sect_name, size_t sect_size)
169 {
170 if (strcmp (sect_name, ".reg") == 0 && sect_size >= ALPHANBSD_SIZEOF_GREGS)
171 {
172 if (sect_size >= ALPHANBSD_SIZEOF_GREGS + ALPHANBSD_SIZEOF_FPREGS)
173 return &alphanbsd_aout_gregset;
174 else
175 return &alphanbsd_gregset;
176 }
177
178 if (strcmp (sect_name, ".reg2") == 0 && sect_size >= ALPHANBSD_SIZEOF_FPREGS)
179 return &alphanbsd_fpregset;
180
181 return NULL;
182 }
183 \f
184
185 /* Signal trampolines. */
186
187 /* Under NetBSD/alpha, signal handler invocations can be identified by the
188 designated code sequence that is used to return from a signal handler.
189 In particular, the return address of a signal handler points to the
190 following code sequence:
191
192 ldq a0, 0(sp)
193 lda sp, 16(sp)
194 lda v0, 295(zero) # __sigreturn14
195 call_pal callsys
196
197 Each instruction has a unique encoding, so we simply attempt to match
198 the instruction the PC is pointing to with any of the above instructions.
199 If there is a hit, we know the offset to the start of the designated
200 sequence and can then check whether we really are executing in the
201 signal trampoline. If not, -1 is returned, otherwise the offset from the
202 start of the return sequence is returned. */
203 static const unsigned char sigtramp_retcode[] =
204 {
205 0x00, 0x00, 0x1e, 0xa6, /* ldq a0, 0(sp) */
206 0x10, 0x00, 0xde, 0x23, /* lda sp, 16(sp) */
207 0x27, 0x01, 0x1f, 0x20, /* lda v0, 295(zero) */
208 0x83, 0x00, 0x00, 0x00, /* call_pal callsys */
209 };
210 #define RETCODE_NWORDS 4
211 #define RETCODE_SIZE (RETCODE_NWORDS * 4)
212
213 static LONGEST
214 alphanbsd_sigtramp_offset (struct gdbarch *gdbarch, CORE_ADDR pc)
215 {
216 unsigned char ret[RETCODE_SIZE], w[4];
217 LONGEST off;
218 int i;
219
220 if (target_read_memory (pc, (char *) w, 4) != 0)
221 return -1;
222
223 for (i = 0; i < RETCODE_NWORDS; i++)
224 {
225 if (memcmp (w, sigtramp_retcode + (i * 4), 4) == 0)
226 break;
227 }
228 if (i == RETCODE_NWORDS)
229 return (-1);
230
231 off = i * 4;
232 pc -= off;
233
234 if (target_read_memory (pc, (char *) ret, sizeof (ret)) != 0)
235 return -1;
236
237 if (memcmp (ret, sigtramp_retcode, RETCODE_SIZE) == 0)
238 return off;
239
240 return -1;
241 }
242
243 static int
244 alphanbsd_pc_in_sigtramp (struct gdbarch *gdbarch,
245 CORE_ADDR pc, char *func_name)
246 {
247 return (nbsd_pc_in_sigtramp (pc, func_name)
248 || alphanbsd_sigtramp_offset (gdbarch, pc) >= 0);
249 }
250
251 static CORE_ADDR
252 alphanbsd_sigcontext_addr (struct frame_info *frame)
253 {
254 /* FIXME: This is not correct for all versions of NetBSD/alpha.
255 We will probably need to disassemble the trampoline to figure
256 out which trampoline frame type we have. */
257 if (!get_next_frame (frame))
258 return 0;
259 return get_frame_base (get_next_frame (frame));
260 }
261 \f
262
263 static void
264 alphanbsd_init_abi (struct gdbarch_info info,
265 struct gdbarch *gdbarch)
266 {
267 struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
268
269 /* Hook into the DWARF CFI frame unwinder. */
270 alpha_dwarf2_init_abi (info, gdbarch);
271
272 /* Hook into the MDEBUG frame unwinder. */
273 alpha_mdebug_init_abi (info, gdbarch);
274
275 /* NetBSD/alpha does not provide single step support via ptrace(2); we
276 must use software single-stepping. */
277 set_gdbarch_software_single_step (gdbarch, alpha_software_single_step);
278
279 /* NetBSD/alpha has SVR4-style shared libraries. */
280 set_solib_svr4_fetch_link_map_offsets
281 (gdbarch, svr4_lp64_fetch_link_map_offsets);
282
283 tdep->dynamic_sigtramp_offset = alphanbsd_sigtramp_offset;
284 tdep->pc_in_sigtramp = alphanbsd_pc_in_sigtramp;
285 tdep->sigcontext_addr = alphanbsd_sigcontext_addr;
286
287 tdep->jb_pc = 2;
288 tdep->jb_elt_size = 8;
289
290 set_gdbarch_regset_from_core_section
291 (gdbarch, alphanbsd_regset_from_core_section);
292 }
293 \f
294
295 static enum gdb_osabi
296 alphanbsd_core_osabi_sniffer (bfd *abfd)
297 {
298 if (strcmp (bfd_get_target (abfd), "netbsd-core") == 0)
299 return GDB_OSABI_NETBSD_ELF;
300
301 return GDB_OSABI_UNKNOWN;
302 }
303 \f
304
305 /* Provide a prototype to silence -Wmissing-prototypes. */
306 void _initialize_alphanbsd_tdep (void);
307
308 void
309 _initialize_alphanbsd_tdep (void)
310 {
311 /* BFD doesn't set a flavour for NetBSD style a.out core files. */
312 gdbarch_register_osabi_sniffer (bfd_arch_alpha, bfd_target_unknown_flavour,
313 alphanbsd_core_osabi_sniffer);
314
315 gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_NETBSD_ELF,
316 alphanbsd_init_abi);
317 }
This page took 0.057734 seconds and 4 git commands to generate.