]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/alphanbsd-tdep.c
Copyright updates for 2007.
[thirdparty/binutils-gdb.git] / gdb / alphanbsd-tdep.c
1 /* Target-dependent code for NetBSD/alpha.
2
3 Copyright (C) 2002, 2003, 2004, 2006, 2007 Free Software Foundation, Inc.
4
5 Contributed by Wasabi Systems, Inc.
6
7 This file is part of GDB.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA. */
23
24 #include "defs.h"
25 #include "frame.h"
26 #include "gdbcore.h"
27 #include "osabi.h"
28 #include "regcache.h"
29 #include "regset.h"
30 #include "value.h"
31
32 #include "gdb_assert.h"
33 #include "gdb_string.h"
34
35 #include "alpha-tdep.h"
36 #include "alphabsd-tdep.h"
37 #include "nbsd-tdep.h"
38 #include "solib-svr4.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 (CORE_ADDR pc)
215 {
216 unsigned char ret[RETCODE_SIZE], w[4];
217 LONGEST off;
218 int i;
219
220 if (read_memory_nobpt (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 (read_memory_nobpt (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 (CORE_ADDR pc, char *func_name)
245 {
246 return (nbsd_pc_in_sigtramp (pc, func_name)
247 || alphanbsd_sigtramp_offset (pc) >= 0);
248 }
249
250 static CORE_ADDR
251 alphanbsd_sigcontext_addr (struct frame_info *frame)
252 {
253 /* FIXME: This is not correct for all versions of NetBSD/alpha.
254 We will probably need to disassemble the trampoline to figure
255 out which trampoline frame type we have. */
256 return get_frame_base (frame);
257 }
258 \f
259
260 static void
261 alphanbsd_init_abi (struct gdbarch_info info,
262 struct gdbarch *gdbarch)
263 {
264 struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
265
266 /* Hook into the DWARF CFI frame unwinder. */
267 alpha_dwarf2_init_abi (info, gdbarch);
268
269 /* Hook into the MDEBUG frame unwinder. */
270 alpha_mdebug_init_abi (info, gdbarch);
271
272 /* NetBSD/alpha does not provide single step support via ptrace(2); we
273 must use software single-stepping. */
274 set_gdbarch_software_single_step (gdbarch, alpha_software_single_step);
275
276 /* NetBSD/alpha has SVR4-style shared libraries. */
277 set_solib_svr4_fetch_link_map_offsets
278 (gdbarch, svr4_lp64_fetch_link_map_offsets);
279
280 tdep->dynamic_sigtramp_offset = alphanbsd_sigtramp_offset;
281 tdep->pc_in_sigtramp = alphanbsd_pc_in_sigtramp;
282 tdep->sigcontext_addr = alphanbsd_sigcontext_addr;
283
284 tdep->jb_pc = 2;
285 tdep->jb_elt_size = 8;
286
287 set_gdbarch_regset_from_core_section
288 (gdbarch, alphanbsd_regset_from_core_section);
289 }
290 \f
291
292 static enum gdb_osabi
293 alphanbsd_core_osabi_sniffer (bfd *abfd)
294 {
295 if (strcmp (bfd_get_target (abfd), "netbsd-core") == 0)
296 return GDB_OSABI_NETBSD_ELF;
297
298 return GDB_OSABI_UNKNOWN;
299 }
300 \f
301
302 /* Provide a prototype to silence -Wmissing-prototypes. */
303 void _initialize_alphanbsd_tdep (void);
304
305 void
306 _initialize_alphanbsd_tdep (void)
307 {
308 /* BFD doesn't set a flavour for NetBSD style a.out core files. */
309 gdbarch_register_osabi_sniffer (bfd_arch_alpha, bfd_target_unknown_flavour,
310 alphanbsd_core_osabi_sniffer);
311
312 gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_NETBSD_ELF,
313 alphanbsd_init_abi);
314 }