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