* amd64-tdep.c (amd64_sigtramp_frame_sniffer): Rewrite to use new
[platform/upstream/binutils.git] / gdb / i386nbsd-tdep.c
1 /* Target-dependent code for NetBSD/i386.
2
3    Copyright 1988, 1989, 1991, 1992, 1994, 1996, 2000, 2001, 2002,
4    2003, 2004
5    Free Software Foundation, 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 2 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, write to the Free Software
21    Foundation, Inc., 59 Temple Place - Suite 330,
22    Boston, MA 02111-1307, USA.  */
23
24 #include "defs.h"
25 #include "arch-utils.h"
26 #include "frame.h"
27 #include "gdbcore.h"
28 #include "regcache.h"
29 #include "regset.h"
30 #include "osabi.h"
31 #include "symtab.h"
32
33 #include "gdb_assert.h"
34 #include "gdb_string.h"
35
36 #include "i386-tdep.h"
37 #include "i387-tdep.h"
38 #include "nbsd-tdep.h"
39 #include "solib-svr4.h"
40
41 /* From <machine/reg.h>.  */
42 static int i386nbsd_r_reg_offset[] =
43 {
44   0 * 4,                        /* %eax */
45   1 * 4,                        /* %ecx */
46   2 * 4,                        /* %edx */
47   3 * 4,                        /* %ebx */
48   4 * 4,                        /* %esp */
49   5 * 4,                        /* %ebp */
50   6 * 4,                        /* %esi */
51   7 * 4,                        /* %edi */
52   8 * 4,                        /* %eip */
53   9 * 4,                        /* %eflags */
54   10 * 4,                       /* %cs */
55   11 * 4,                       /* %ss */
56   12 * 4,                       /* %ds */
57   13 * 4,                       /* %es */
58   14 * 4,                       /* %fs */
59   15 * 4                        /* %gs */
60 };
61
62 static void
63 i386nbsd_aout_supply_regset (const struct regset *regset,
64                              struct regcache *regcache, int regnum,
65                              const void *regs, size_t len)
66 {
67   const struct gdbarch_tdep *tdep = regset->descr;
68
69   gdb_assert (len >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE);
70
71   i386_supply_gregset (regset, regcache, regnum, regs, tdep->sizeof_gregset);
72   i387_supply_fsave (regcache, regnum, (char *) regs + tdep->sizeof_gregset);
73 }
74
75 static const struct regset *
76 i386nbsd_aout_regset_from_core_section (struct gdbarch *gdbarch,
77                                         const char *sect_name,
78                                         size_t sect_size)
79 {
80   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
81
82   /* NetBSD a.out core dumps don't use seperate register sets for the
83      general-purpose and floating-point registers.  */
84
85   if (strcmp (sect_name, ".reg") == 0
86       && sect_size >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE)
87     {
88       if (tdep->gregset == NULL)
89         {
90           tdep->gregset = XMALLOC (struct regset);
91           tdep->gregset->descr = tdep;
92           tdep->gregset->supply_regset = i386nbsd_aout_supply_regset;
93         }
94       return tdep->gregset;
95     }
96
97   return NULL;
98 }
99
100 /* Under NetBSD/i386, signal handler invocations can be identified by the
101    designated code sequence that is used to return from a signal handler.
102    In particular, the return address of a signal handler points to the
103    following code sequence:
104
105         leal    0x10(%esp), %eax
106         pushl   %eax
107         pushl   %eax
108         movl    $0x127, %eax            # __sigreturn14
109         int     $0x80
110
111    Each instruction has a unique encoding, so we simply attempt to match
112    the instruction the PC is pointing to with any of the above instructions.
113    If there is a hit, we know the offset to the start of the designated
114    sequence and can then check whether we really are executing in the
115    signal trampoline.  If not, -1 is returned, otherwise the offset from the
116    start of the return sequence is returned.  */
117 #define RETCODE_INSN1           0x8d
118 #define RETCODE_INSN2           0x50
119 #define RETCODE_INSN3           0x50
120 #define RETCODE_INSN4           0xb8
121 #define RETCODE_INSN5           0xcd
122
123 #define RETCODE_INSN2_OFF       4
124 #define RETCODE_INSN3_OFF       5
125 #define RETCODE_INSN4_OFF       6
126 #define RETCODE_INSN5_OFF       11
127
128 static const unsigned char sigtramp_retcode[] =
129 {
130   RETCODE_INSN1, 0x44, 0x24, 0x10,
131   RETCODE_INSN2,
132   RETCODE_INSN3,
133   RETCODE_INSN4, 0x27, 0x01, 0x00, 0x00,
134   RETCODE_INSN5, 0x80,
135 };
136
137 static LONGEST
138 i386nbsd_sigtramp_offset (CORE_ADDR pc)
139 {
140   unsigned char ret[sizeof(sigtramp_retcode)], insn;
141   LONGEST off;
142   int i;
143
144   if (read_memory_nobpt (pc, &insn, 1) != 0)
145     return -1;
146
147   switch (insn)
148     {
149     case RETCODE_INSN1:
150       off = 0;
151       break;
152
153     case RETCODE_INSN2:
154       /* INSN2 and INSN3 are the same.  Read at the location of PC+1
155          to determine if we're actually looking at INSN2 or INSN3.  */
156       if (read_memory_nobpt (pc + 1, &insn, 1) != 0)
157         return -1;
158
159       if (insn == RETCODE_INSN3)
160         off = RETCODE_INSN2_OFF;
161       else
162         off = RETCODE_INSN3_OFF;
163       break;
164
165     case RETCODE_INSN4:
166       off = RETCODE_INSN4_OFF;
167       break;
168
169     case RETCODE_INSN5:
170       off = RETCODE_INSN5_OFF;
171       break;
172
173     default:
174       return -1;
175     }
176
177   pc -= off;
178
179   if (read_memory_nobpt (pc, (char *) ret, sizeof (ret)) != 0)
180     return -1;
181
182   if (memcmp (ret, sigtramp_retcode, sizeof (ret)) == 0)
183     return off;
184
185   return -1;
186 }
187
188 /* Return whether the frame preciding NEXT_FRAME corresponds to a
189    NetBSD sigtramp routine.  */
190
191 static int
192 i386nbsd_sigtramp_p (struct frame_info *next_frame)
193 {
194   CORE_ADDR pc = frame_pc_unwind (next_frame);
195   char *name;
196
197   find_pc_partial_function (pc, &name, NULL, NULL);
198   return (nbsd_pc_in_sigtramp (pc, name)
199           || i386nbsd_sigtramp_offset (pc) >= 0);
200 }
201
202 /* From <machine/signal.h>.  */
203 int i386nbsd_sc_reg_offset[] =
204 {
205   10 * 4,                       /* %eax */
206   9 * 4,                        /* %ecx */
207   8 * 4,                        /* %edx */
208   7 * 4,                        /* %ebx */
209   14 * 4,                       /* %esp */
210   6 * 4,                        /* %ebp */
211   5 * 4,                        /* %esi */
212   4 * 4,                        /* %edi */
213   11 * 4,                       /* %eip */
214   13 * 4,                       /* %eflags */
215   12 * 4,                       /* %cs */
216   15 * 4,                       /* %ss */
217   3 * 4,                        /* %ds */
218   2 * 4,                        /* %es */
219   1 * 4,                        /* %fs */
220   0 * 4                         /* %gs */
221 };
222
223 static void 
224 i386nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
225 {
226   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
227
228   /* Obviously NetBSD is BSD-based.  */
229   i386bsd_init_abi (info, gdbarch);
230
231   /* NetBSD has a different `struct reg'.  */
232   tdep->gregset_reg_offset = i386nbsd_r_reg_offset;
233   tdep->gregset_num_regs = ARRAY_SIZE (i386nbsd_r_reg_offset);
234   tdep->sizeof_gregset = 16 * 4;
235
236   /* NetBSD has different signal trampoline conventions.  */
237   tdep->sigtramp_start = 0;
238   tdep->sigtramp_end = 0;
239   tdep->sigtramp_p = i386nbsd_sigtramp_p;
240
241   /* NetBSD uses -freg-struct-return by default.  */
242   tdep->struct_return = reg_struct_return;
243
244   /* NetBSD has a `struct sigcontext' that's different from the
245      original 4.3 BSD.  */
246   tdep->sc_reg_offset = i386nbsd_sc_reg_offset;
247   tdep->sc_num_regs = ARRAY_SIZE (i386nbsd_sc_reg_offset);
248 }
249
250 /* NetBSD a.out.  */
251
252 static void
253 i386nbsdaout_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
254 {
255   i386nbsd_init_abi (info, gdbarch);
256
257   /* NetBSD a.out has a single register set.  */
258   set_gdbarch_regset_from_core_section
259     (gdbarch, i386nbsd_aout_regset_from_core_section);
260 }
261
262 /* NetBSD ELF.  */
263
264 static void
265 i386nbsdelf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
266 {
267   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
268
269   /* It's still NetBSD.  */
270   i386nbsd_init_abi (info, gdbarch);
271
272   /* But ELF-based.  */
273   i386_elf_init_abi (info, gdbarch);
274
275   /* NetBSD ELF uses SVR4-style shared libraries.  */
276   set_gdbarch_in_solib_call_trampoline
277     (gdbarch, generic_in_solib_call_trampoline);
278   set_solib_svr4_fetch_link_map_offsets
279     (gdbarch, svr4_ilp32_fetch_link_map_offsets);
280
281   /* NetBSD ELF uses -fpcc-struct-return by default.  */
282   tdep->struct_return = pcc_struct_return;
283 }
284
285 void
286 _initialize_i386nbsd_tdep (void)
287 {
288   gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_AOUT,
289                           i386nbsdaout_init_abi);
290   gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_ELF,
291                           i386nbsdelf_init_abi);
292 }