Updated copyright notices for most files.
[external/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
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 (CORE_ADDR pc)
214 {
215   unsigned char ret[RETCODE_SIZE], w[4];
216   LONGEST off;
217   int i;
218
219   if (read_memory_nobpt (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 (read_memory_nobpt (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 (CORE_ADDR pc, char *func_name)
244 {
245   return (nbsd_pc_in_sigtramp (pc, func_name)
246           || alphanbsd_sigtramp_offset (pc) >= 0);
247 }
248
249 static CORE_ADDR
250 alphanbsd_sigcontext_addr (struct frame_info *frame)
251 {
252   /* FIXME: This is not correct for all versions of NetBSD/alpha.
253      We will probably need to disassemble the trampoline to figure
254      out which trampoline frame type we have.  */
255   return get_frame_base (frame);
256 }
257 \f
258
259 static void
260 alphanbsd_init_abi (struct gdbarch_info info,
261                     struct gdbarch *gdbarch)
262 {
263   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
264
265   /* Hook into the DWARF CFI frame unwinder.  */
266   alpha_dwarf2_init_abi (info, gdbarch);
267
268   /* Hook into the MDEBUG frame unwinder.  */
269   alpha_mdebug_init_abi (info, gdbarch);
270
271   /* NetBSD/alpha does not provide single step support via ptrace(2); we
272      must use software single-stepping.  */
273   set_gdbarch_software_single_step (gdbarch, alpha_software_single_step);
274
275   /* NetBSD/alpha has SVR4-style shared libraries.  */
276   set_solib_svr4_fetch_link_map_offsets
277     (gdbarch, svr4_lp64_fetch_link_map_offsets);
278
279   tdep->dynamic_sigtramp_offset = alphanbsd_sigtramp_offset;
280   tdep->pc_in_sigtramp = alphanbsd_pc_in_sigtramp;
281   tdep->sigcontext_addr = alphanbsd_sigcontext_addr;
282
283   tdep->jb_pc = 2;
284   tdep->jb_elt_size = 8;
285
286   set_gdbarch_regset_from_core_section
287     (gdbarch, alphanbsd_regset_from_core_section);
288 }
289 \f
290
291 static enum gdb_osabi
292 alphanbsd_core_osabi_sniffer (bfd *abfd)
293 {
294   if (strcmp (bfd_get_target (abfd), "netbsd-core") == 0)
295     return GDB_OSABI_NETBSD_ELF;
296
297   return GDB_OSABI_UNKNOWN;
298 }
299 \f
300
301 /* Provide a prototype to silence -Wmissing-prototypes.  */
302 void _initialize_alphanbsd_tdep (void);
303
304 void
305 _initialize_alphanbsd_tdep (void)
306 {
307   /* BFD doesn't set a flavour for NetBSD style a.out core files.  */
308   gdbarch_register_osabi_sniffer (bfd_arch_alpha, bfd_target_unknown_flavour,
309                                   alphanbsd_core_osabi_sniffer);
310
311   gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_NETBSD_ELF,
312                           alphanbsd_init_abi);
313 }