2007-11-16 Markus Deuling <deuling@de.ibm.com>
[platform/upstream/binutils.git] / gdb / ppcobsd-tdep.c
1 /* Target-dependent code for OpenBSD/powerpc.
2
3    Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
4
5    This file is part of GDB.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20 #include "defs.h"
21 #include "arch-utils.h"
22 #include "frame.h"
23 #include "frame-unwind.h"
24 #include "gdbtypes.h"
25 #include "osabi.h"
26 #include "regcache.h"
27 #include "regset.h"
28 #include "symtab.h"
29 #include "trad-frame.h"
30
31 #include "gdb_assert.h"
32 #include "gdb_string.h"
33
34 #include "ppc-tdep.h"
35 #include "ppcobsd-tdep.h"
36 #include "solib-svr4.h"
37
38 /* Register offsets from <machine/reg.h>.  */
39 struct ppc_reg_offsets ppcobsd_reg_offsets;
40 struct ppc_reg_offsets ppcobsd_fpreg_offsets;
41 \f
42
43 /* Core file support.  */
44
45 /* Supply register REGNUM in the general-purpose register set REGSET
46    from the buffer specified by GREGS and LEN to register cache
47    REGCACHE.  If REGNUM is -1, do this for all registers in REGSET.  */
48
49 void
50 ppcobsd_supply_gregset (const struct regset *regset,
51                         struct regcache *regcache, int regnum,
52                         const void *gregs, size_t len)
53 {
54   ppc_supply_gregset (regset, regcache, regnum, gregs, len);
55   ppc_supply_fpregset (regset, regcache, regnum, gregs, len);
56 }
57
58 /* Collect register REGNUM in the general-purpose register set
59    REGSET. from register cache REGCACHE into the buffer specified by
60    GREGS and LEN.  If REGNUM is -1, do this for all registers in
61    REGSET.  */
62
63 void
64 ppcobsd_collect_gregset (const struct regset *regset,
65                          const struct regcache *regcache, int regnum,
66                          void *gregs, size_t len)
67 {
68   ppc_collect_gregset (regset, regcache, regnum, gregs, len);
69   ppc_collect_fpregset (regset, regcache, regnum, gregs, len);
70 }
71
72 /* OpenBSD/powerpc register set.  */
73
74 struct regset ppcobsd_gregset =
75 {
76   &ppcobsd_reg_offsets,
77   ppcobsd_supply_gregset
78 };
79
80 struct regset ppcobsd_fpregset =
81 {
82   &ppcobsd_fpreg_offsets,
83   ppc_supply_fpregset
84 };
85
86 /* Return the appropriate register set for the core section identified
87    by SECT_NAME and SECT_SIZE.  */
88
89 static const struct regset *
90 ppcobsd_regset_from_core_section (struct gdbarch *gdbarch,
91                                   const char *sect_name, size_t sect_size)
92 {
93   if (strcmp (sect_name, ".reg") == 0 && sect_size >= 412)
94     return &ppcobsd_gregset;
95
96   return NULL;
97 }
98 \f
99
100 /* Signal trampolines.  */
101
102 /* Since OpenBSD 3.2, the sigtramp routine is mapped at a random page
103    in virtual memory.  The randomness makes it somewhat tricky to
104    detect it, but fortunately we can rely on the fact that the start
105    of the sigtramp routine is page-aligned.  We recognize the
106    trampoline by looking for the code that invokes the sigreturn
107    system call.  The offset where we can find that code varies from
108    release to release.
109
110    By the way, the mapping mentioned above is read-only, so you cannot
111    place a breakpoint in the signal trampoline.  */
112
113 /* Default page size.  */
114 static const int ppcobsd_page_size = 4096;
115
116 /* Offset for sigreturn(2).  */
117 static const int ppcobsd_sigreturn_offset[] = {
118   0x98,                         /* OpenBSD 3.8 */
119   0x0c,                         /* OpenBSD 3.2 */
120   -1
121 };
122
123 static int
124 ppcobsd_sigtramp_p (struct frame_info *next_frame)
125 {
126   CORE_ADDR pc = frame_pc_unwind (next_frame);
127   CORE_ADDR start_pc = (pc & ~(ppcobsd_page_size - 1));
128   const int *offset;
129   char *name;
130
131   find_pc_partial_function (pc, &name, NULL, NULL);
132   if (name)
133     return 0;
134
135   for (offset = ppcobsd_sigreturn_offset; *offset != -1; offset++)
136     {
137       gdb_byte buf[2 * PPC_INSN_SIZE];
138       unsigned long insn;
139
140       if (!safe_frame_unwind_memory (next_frame, start_pc + *offset,
141                                      buf, sizeof buf))
142         continue;
143
144       /* Check for "li r0,SYS_sigreturn".  */
145       insn = extract_unsigned_integer (buf, PPC_INSN_SIZE);
146       if (insn != 0x38000067)
147         continue;
148
149       /* Check for "sc".  */
150       insn = extract_unsigned_integer (buf + PPC_INSN_SIZE, PPC_INSN_SIZE);
151       if (insn != 0x44000002)
152         continue;
153
154       return 1;
155     }
156
157   return 0;
158 }
159
160 static struct trad_frame_cache *
161 ppcobsd_sigtramp_frame_cache (struct frame_info *next_frame, void **this_cache)
162 {
163   struct gdbarch *gdbarch = get_frame_arch (next_frame);
164   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
165   struct trad_frame_cache *cache;
166   CORE_ADDR addr, base, func;
167   gdb_byte buf[PPC_INSN_SIZE];
168   unsigned long insn, sigcontext_offset;
169   int i;
170
171   if (*this_cache)
172     return *this_cache;
173
174   cache = trad_frame_cache_zalloc (next_frame);
175   *this_cache = cache;
176
177   func = frame_pc_unwind (next_frame);
178   func &= ~(ppcobsd_page_size - 1);
179   if (!safe_frame_unwind_memory (next_frame, func, buf, sizeof buf))
180     return cache;
181
182   /* Calculate the offset where we can find `struct sigcontext'.  We
183      base our calculation on the amount of stack space reserved by the
184      first instruction of the signal trampoline.  */
185   insn = extract_unsigned_integer (buf, PPC_INSN_SIZE);
186   sigcontext_offset = (0x10000 - (insn & 0x0000ffff)) + 8;
187
188   base = frame_unwind_register_unsigned (next_frame,
189                                          gdbarch_sp_regnum (gdbarch));
190   addr = base + sigcontext_offset + 2 * tdep->wordsize;
191   for (i = 0; i < ppc_num_gprs; i++, addr += tdep->wordsize)
192     {
193       int regnum = i + tdep->ppc_gp0_regnum;
194       trad_frame_set_reg_addr (cache, regnum, addr);
195     }
196   trad_frame_set_reg_addr (cache, tdep->ppc_lr_regnum, addr);
197   addr += tdep->wordsize;
198   trad_frame_set_reg_addr (cache, tdep->ppc_cr_regnum, addr);
199   addr += tdep->wordsize;
200   trad_frame_set_reg_addr (cache, tdep->ppc_xer_regnum, addr);
201   addr += tdep->wordsize;
202   trad_frame_set_reg_addr (cache, tdep->ppc_ctr_regnum, addr);
203   addr += tdep->wordsize;
204   trad_frame_set_reg_addr (cache, gdbarch_pc_regnum (gdbarch), addr);
205   /* SRR0? */
206   addr += tdep->wordsize;
207
208   /* Construct the frame ID using the function start.  */
209   trad_frame_set_id (cache, frame_id_build (base, func));
210
211   return cache;
212 }
213
214 static void
215 ppcobsd_sigtramp_frame_this_id (struct frame_info *next_frame,
216                                 void **this_cache, struct frame_id *this_id)
217 {
218   struct trad_frame_cache *cache =
219     ppcobsd_sigtramp_frame_cache (next_frame, this_cache);
220
221   trad_frame_get_id (cache, this_id);
222 }
223
224 static void
225 ppcobsd_sigtramp_frame_prev_register (struct frame_info *next_frame,
226                                       void **this_cache, int regnum,
227                                       int *optimizedp, enum lval_type *lvalp,
228                                       CORE_ADDR *addrp, int *realnump,
229                                       gdb_byte *valuep)
230 {
231   struct trad_frame_cache *cache =
232     ppcobsd_sigtramp_frame_cache (next_frame, this_cache);
233
234   trad_frame_get_register (cache, next_frame, regnum,
235                            optimizedp, lvalp, addrp, realnump, valuep);
236 }
237
238 static const struct frame_unwind ppcobsd_sigtramp_frame_unwind = {
239   SIGTRAMP_FRAME,
240   ppcobsd_sigtramp_frame_this_id,
241   ppcobsd_sigtramp_frame_prev_register
242 };
243
244 static const struct frame_unwind *
245 ppcobsd_sigtramp_frame_sniffer (struct frame_info *next_frame)
246 {
247   if (ppcobsd_sigtramp_p (next_frame))
248     return &ppcobsd_sigtramp_frame_unwind;
249
250   return NULL;
251 }
252 \f
253
254 static void
255 ppcobsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
256 {
257   /* OpenBSD doesn't support the 128-bit `long double' from the psABI.  */
258   set_gdbarch_long_double_bit (gdbarch, 64);
259   set_gdbarch_long_double_format (gdbarch, floatformats_ieee_double);
260
261   /* OpenBSD currently uses a broken GCC.  */
262   set_gdbarch_return_value (gdbarch, ppc_sysv_abi_broken_return_value);
263
264   /* OpenBSD uses SVR4-style shared libraries.  */
265   set_solib_svr4_fetch_link_map_offsets
266     (gdbarch, svr4_ilp32_fetch_link_map_offsets);
267
268   set_gdbarch_regset_from_core_section
269     (gdbarch, ppcobsd_regset_from_core_section);
270
271   frame_unwind_append_sniffer (gdbarch, ppcobsd_sigtramp_frame_sniffer);
272 }
273 \f
274
275 /* OpenBSD uses uses the traditional NetBSD core file format, even for
276    ports that use ELF.  */
277 #define GDB_OSABI_NETBSD_CORE GDB_OSABI_OPENBSD_ELF
278
279 static enum gdb_osabi
280 ppcobsd_core_osabi_sniffer (bfd *abfd)
281 {
282   if (strcmp (bfd_get_target (abfd), "netbsd-core") == 0)
283     return GDB_OSABI_NETBSD_CORE;
284
285   return GDB_OSABI_UNKNOWN;
286 }
287 \f
288
289 /* Provide a prototype to silence -Wmissing-prototypes.  */
290 void _initialize_ppcobsd_tdep (void);
291
292 void
293 _initialize_ppcobsd_tdep (void)
294 {
295   /* BFD doesn't set a flavour for NetBSD style a.out core files.  */
296   gdbarch_register_osabi_sniffer (bfd_arch_powerpc, bfd_target_unknown_flavour,
297                                   ppcobsd_core_osabi_sniffer);
298
299   gdbarch_register_osabi (bfd_arch_rs6000, 0, GDB_OSABI_OPENBSD_ELF,
300                           ppcobsd_init_abi);
301   gdbarch_register_osabi (bfd_arch_powerpc, 0, GDB_OSABI_OPENBSD_ELF,
302                           ppcobsd_init_abi);
303
304   /* Avoid initializing the register offsets again if they were
305      already initailized by ppcobsd-nat.c.  */
306   if (ppcobsd_reg_offsets.pc_offset == 0)
307     {
308       /* General-purpose registers.  */
309       ppcobsd_reg_offsets.r0_offset = 0;
310       ppcobsd_reg_offsets.gpr_size = 4;
311       ppcobsd_reg_offsets.xr_size = 4;
312       ppcobsd_reg_offsets.pc_offset = 384;
313       ppcobsd_reg_offsets.ps_offset = 388;
314       ppcobsd_reg_offsets.cr_offset = 392;
315       ppcobsd_reg_offsets.lr_offset = 396;
316       ppcobsd_reg_offsets.ctr_offset = 400;
317       ppcobsd_reg_offsets.xer_offset = 404;
318       ppcobsd_reg_offsets.mq_offset = 408;
319
320       /* Floating-point registers.  */
321       ppcobsd_reg_offsets.f0_offset = 128;
322       ppcobsd_reg_offsets.fpscr_offset = -1;
323
324       /* AltiVec registers.  */
325       ppcobsd_reg_offsets.vr0_offset = 0;
326       ppcobsd_reg_offsets.vscr_offset = 512;
327       ppcobsd_reg_offsets.vrsave_offset = 520;
328     }
329
330   if (ppcobsd_fpreg_offsets.fpscr_offset == 0)
331     {
332       /* Floating-point registers.  */
333       ppcobsd_reg_offsets.f0_offset = 0;
334       ppcobsd_reg_offsets.fpscr_offset = 256;
335       ppcobsd_reg_offsets.fpscr_size = 4;
336     }
337 }