ld/testsuite/
[external/binutils.git] / gdb / alphanbsd-tdep.c
1 /* Target-dependent code for NetBSD/alpha.
2
3    Copyright (C) 2002-2004, 2006-2012 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 "gdb_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 unsigned char 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   unsigned char ret[RETCODE_SIZE], w[4];
216   LONGEST off;
217   int i;
218
219   if (target_read_memory (pc, (char *) 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, (char *) 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 }