PR symtab/11198:
[platform/upstream/binutils.git] / gdb / alphanbsd-tdep.c
1 /* Target-dependent code for NetBSD/alpha.
2
3    Copyright (C) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010
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 }