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>.  */
42static 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
62static void
63i386nbsd_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 = gdbarch_tdep (regset->arch);
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
75static const struct regset *
76i386nbsd_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        tdep->gregset =
90	  regset_alloc (gdbarch, i386nbsd_aout_supply_regset, NULL);
91      return tdep->gregset;
92    }
93
94  return NULL;
95}
96
97/* Under NetBSD/i386, signal handler invocations can be identified by the
98   designated code sequence that is used to return from a signal handler.
99   In particular, the return address of a signal handler points to the
100   following code sequence:
101
102	leal	0x10(%esp), %eax
103	pushl	%eax
104	pushl	%eax
105	movl	$0x127, %eax		# __sigreturn14
106	int	$0x80
107
108   Each instruction has a unique encoding, so we simply attempt to match
109   the instruction the PC is pointing to with any of the above instructions.
110   If there is a hit, we know the offset to the start of the designated
111   sequence and can then check whether we really are executing in the
112   signal trampoline.  If not, -1 is returned, otherwise the offset from the
113   start of the return sequence is returned.  */
114#define RETCODE_INSN1		0x8d
115#define RETCODE_INSN2		0x50
116#define RETCODE_INSN3		0x50
117#define RETCODE_INSN4		0xb8
118#define RETCODE_INSN5		0xcd
119
120#define RETCODE_INSN2_OFF	4
121#define RETCODE_INSN3_OFF	5
122#define RETCODE_INSN4_OFF	6
123#define RETCODE_INSN5_OFF	11
124
125static const unsigned char sigtramp_retcode[] =
126{
127  RETCODE_INSN1, 0x44, 0x24, 0x10,
128  RETCODE_INSN2,
129  RETCODE_INSN3,
130  RETCODE_INSN4, 0x27, 0x01, 0x00, 0x00,
131  RETCODE_INSN5, 0x80,
132};
133
134static LONGEST
135i386nbsd_sigtramp_offset (struct frame_info *next_frame)
136{
137  CORE_ADDR pc = frame_pc_unwind (next_frame);
138  unsigned char ret[sizeof(sigtramp_retcode)], insn;
139  LONGEST off;
140  int i;
141
142  if (!safe_frame_unwind_memory (next_frame, pc, &insn, 1))
143    return -1;
144
145  switch (insn)
146    {
147    case RETCODE_INSN1:
148      off = 0;
149      break;
150
151    case RETCODE_INSN2:
152      /* INSN2 and INSN3 are the same.  Read at the location of PC+1
153	 to determine if we're actually looking at INSN2 or INSN3.  */
154      if (!safe_frame_unwind_memory (next_frame, pc + 1, &insn, 1))
155	return -1;
156
157      if (insn == RETCODE_INSN3)
158	off = RETCODE_INSN2_OFF;
159      else
160	off = RETCODE_INSN3_OFF;
161      break;
162
163    case RETCODE_INSN4:
164      off = RETCODE_INSN4_OFF;
165      break;
166
167    case RETCODE_INSN5:
168      off = RETCODE_INSN5_OFF;
169      break;
170
171    default:
172      return -1;
173    }
174
175  pc -= off;
176
177  if (!safe_frame_unwind_memory (next_frame, pc, ret, sizeof (ret)))
178    return -1;
179
180  if (memcmp (ret, sigtramp_retcode, sizeof (ret)) == 0)
181    return off;
182
183  return -1;
184}
185
186/* Return whether the frame preceding NEXT_FRAME corresponds to a
187   NetBSD sigtramp routine.  */
188
189static int
190i386nbsd_sigtramp_p (struct frame_info *next_frame)
191{
192  CORE_ADDR pc = frame_pc_unwind (next_frame);
193  char *name;
194
195  find_pc_partial_function (pc, &name, NULL, NULL);
196  return (nbsd_pc_in_sigtramp (pc, name)
197	  || i386nbsd_sigtramp_offset (next_frame) >= 0);
198}
199
200/* From <machine/signal.h>.  */
201int i386nbsd_sc_reg_offset[] =
202{
203  10 * 4,			/* %eax */
204  9 * 4,			/* %ecx */
205  8 * 4,			/* %edx */
206  7 * 4,			/* %ebx */
207  14 * 4,			/* %esp */
208  6 * 4,			/* %ebp */
209  5 * 4,			/* %esi */
210  4 * 4,			/* %edi */
211  11 * 4,			/* %eip */
212  13 * 4,			/* %eflags */
213  12 * 4,			/* %cs */
214  15 * 4,			/* %ss */
215  3 * 4,			/* %ds */
216  2 * 4,			/* %es */
217  1 * 4,			/* %fs */
218  0 * 4				/* %gs */
219};
220
221static void
222i386nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
223{
224  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
225
226  /* Obviously NetBSD is BSD-based.  */
227  i386bsd_init_abi (info, gdbarch);
228
229  /* NetBSD has a different `struct reg'.  */
230  tdep->gregset_reg_offset = i386nbsd_r_reg_offset;
231  tdep->gregset_num_regs = ARRAY_SIZE (i386nbsd_r_reg_offset);
232  tdep->sizeof_gregset = 16 * 4;
233
234  /* NetBSD has different signal trampoline conventions.  */
235  tdep->sigtramp_start = 0;
236  tdep->sigtramp_end = 0;
237  tdep->sigtramp_p = i386nbsd_sigtramp_p;
238
239  /* NetBSD uses -freg-struct-return by default.  */
240  tdep->struct_return = reg_struct_return;
241
242  /* NetBSD has a `struct sigcontext' that's different from the
243     original 4.3 BSD.  */
244  tdep->sc_reg_offset = i386nbsd_sc_reg_offset;
245  tdep->sc_num_regs = ARRAY_SIZE (i386nbsd_sc_reg_offset);
246}
247
248/* NetBSD a.out.  */
249
250static void
251i386nbsdaout_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
252{
253  i386nbsd_init_abi (info, gdbarch);
254
255  /* NetBSD a.out has a single register set.  */
256  set_gdbarch_regset_from_core_section
257    (gdbarch, i386nbsd_aout_regset_from_core_section);
258}
259
260/* NetBSD ELF.  */
261
262static void
263i386nbsdelf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
264{
265  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
266
267  /* It's still NetBSD.  */
268  i386nbsd_init_abi (info, gdbarch);
269
270  /* But ELF-based.  */
271  i386_elf_init_abi (info, gdbarch);
272
273  /* NetBSD ELF uses SVR4-style shared libraries.  */
274  set_gdbarch_in_solib_call_trampoline
275    (gdbarch, generic_in_solib_call_trampoline);
276  set_solib_svr4_fetch_link_map_offsets
277    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
278
279  /* NetBSD ELF uses -fpcc-struct-return by default.  */
280  tdep->struct_return = pcc_struct_return;
281}
282
283void
284_initialize_i386nbsd_tdep (void)
285{
286  gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_AOUT,
287			  i386nbsdaout_init_abi);
288  gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_ELF,
289			  i386nbsdelf_init_abi);
290}
291