* alphafbsd-tdep.c: Update for unwinder changes.
[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
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 (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 (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   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 }