1130803Smarcel/* Target-dependent code for NetBSD/i386.
2130803Smarcel
3130803Smarcel   Copyright 1988, 1989, 1991, 1992, 1994, 1996, 2000, 2001, 2002,
4130803Smarcel   2003, 2004
598944Sobrien   Free Software Foundation, Inc.
698944Sobrien
798944Sobrien   This file is part of GDB.
898944Sobrien
998944Sobrien   This program is free software; you can redistribute it and/or modify
1098944Sobrien   it under the terms of the GNU General Public License as published by
1198944Sobrien   the Free Software Foundation; either version 2 of the License, or
1298944Sobrien   (at your option) any later version.
1398944Sobrien
1498944Sobrien   This program is distributed in the hope that it will be useful,
1598944Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
1698944Sobrien   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1798944Sobrien   GNU General Public License for more details.
1898944Sobrien
1998944Sobrien   You should have received a copy of the GNU General Public License
2098944Sobrien   along with this program; if not, write to the Free Software
2198944Sobrien   Foundation, Inc., 59 Temple Place - Suite 330,
2298944Sobrien   Boston, MA 02111-1307, USA.  */
2398944Sobrien
2498944Sobrien#include "defs.h"
25130803Smarcel#include "arch-utils.h"
26130803Smarcel#include "gdbcore.h"
27130803Smarcel#include "regcache.h"
28130803Smarcel#include "regset.h"
29130803Smarcel#include "osabi.h"
3098944Sobrien
31130803Smarcel#include "gdb_assert.h"
32130803Smarcel#include "gdb_string.h"
33130803Smarcel
34130803Smarcel#include "i386-tdep.h"
35130803Smarcel#include "i387-tdep.h"
36130803Smarcel#include "nbsd-tdep.h"
37130803Smarcel#include "solib-svr4.h"
38130803Smarcel
39130803Smarcel/* From <machine/reg.h>.  */
40130803Smarcelstatic int i386nbsd_r_reg_offset[] =
4198944Sobrien{
42130803Smarcel  0 * 4,			/* %eax */
43130803Smarcel  1 * 4,			/* %ecx */
44130803Smarcel  2 * 4,			/* %edx */
45130803Smarcel  3 * 4,			/* %ebx */
46130803Smarcel  4 * 4,			/* %esp */
47130803Smarcel  5 * 4,			/* %ebp */
48130803Smarcel  6 * 4,			/* %esi */
49130803Smarcel  7 * 4,			/* %edi */
50130803Smarcel  8 * 4,			/* %eip */
51130803Smarcel  9 * 4,			/* %eflags */
52130803Smarcel  10 * 4,			/* %cs */
53130803Smarcel  11 * 4,			/* %ss */
54130803Smarcel  12 * 4,			/* %ds */
55130803Smarcel  13 * 4,			/* %es */
56130803Smarcel  14 * 4,			/* %fs */
57130803Smarcel  15 * 4			/* %gs */
58130803Smarcel};
59130803Smarcel
60130803Smarcelstatic void
61130803Smarceli386nbsd_aout_supply_regset (const struct regset *regset,
62130803Smarcel			     struct regcache *regcache, int regnum,
63130803Smarcel			     const void *regs, size_t len)
64130803Smarcel{
65130803Smarcel  const struct gdbarch_tdep *tdep = regset->descr;
66130803Smarcel
67130803Smarcel  gdb_assert (len >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE);
68130803Smarcel
69130803Smarcel  i386_supply_gregset (regset, regcache, regnum, regs, tdep->sizeof_gregset);
70130803Smarcel  i387_supply_fsave (regcache, regnum, (char *) regs + tdep->sizeof_gregset);
7198944Sobrien}
72130803Smarcel
73130803Smarcelstatic const struct regset *
74130803Smarceli386nbsd_aout_regset_from_core_section (struct gdbarch *gdbarch,
75130803Smarcel					const char *sect_name,
76130803Smarcel					size_t sect_size)
77130803Smarcel{
78130803Smarcel  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
79130803Smarcel
80130803Smarcel  /* NetBSD a.out core dumps don't use seperate register sets for the
81130803Smarcel     general-purpose and floating-point registers.  */
82130803Smarcel
83130803Smarcel  if (strcmp (sect_name, ".reg") == 0
84130803Smarcel      && sect_size >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE)
85130803Smarcel    {
86130803Smarcel      if (tdep->gregset == NULL)
87130803Smarcel	{
88130803Smarcel	  tdep->gregset = XMALLOC (struct regset);
89130803Smarcel	  tdep->gregset->descr = tdep;
90130803Smarcel	  tdep->gregset->supply_regset = i386nbsd_aout_supply_regset;
91130803Smarcel	}
92130803Smarcel      return tdep->gregset;
93130803Smarcel    }
94130803Smarcel
95130803Smarcel  return NULL;
96130803Smarcel}
97130803Smarcel
98130803Smarcel/* Under NetBSD/i386, signal handler invocations can be identified by the
99130803Smarcel   designated code sequence that is used to return from a signal handler.
100130803Smarcel   In particular, the return address of a signal handler points to the
101130803Smarcel   following code sequence:
102130803Smarcel
103130803Smarcel	leal	0x10(%esp), %eax
104130803Smarcel	pushl	%eax
105130803Smarcel	pushl	%eax
106130803Smarcel	movl	$0x127, %eax		# __sigreturn14
107130803Smarcel	int	$0x80
108130803Smarcel
109130803Smarcel   Each instruction has a unique encoding, so we simply attempt to match
110130803Smarcel   the instruction the PC is pointing to with any of the above instructions.
111130803Smarcel   If there is a hit, we know the offset to the start of the designated
112130803Smarcel   sequence and can then check whether we really are executing in the
113130803Smarcel   signal trampoline.  If not, -1 is returned, otherwise the offset from the
114130803Smarcel   start of the return sequence is returned.  */
115130803Smarcel#define RETCODE_INSN1		0x8d
116130803Smarcel#define RETCODE_INSN2		0x50
117130803Smarcel#define RETCODE_INSN3		0x50
118130803Smarcel#define RETCODE_INSN4		0xb8
119130803Smarcel#define RETCODE_INSN5		0xcd
120130803Smarcel
121130803Smarcel#define RETCODE_INSN2_OFF	4
122130803Smarcel#define RETCODE_INSN3_OFF	5
123130803Smarcel#define RETCODE_INSN4_OFF	6
124130803Smarcel#define RETCODE_INSN5_OFF	11
125130803Smarcel
126130803Smarcelstatic const unsigned char sigtramp_retcode[] =
127130803Smarcel{
128130803Smarcel  RETCODE_INSN1, 0x44, 0x24, 0x10,
129130803Smarcel  RETCODE_INSN2,
130130803Smarcel  RETCODE_INSN3,
131130803Smarcel  RETCODE_INSN4, 0x27, 0x01, 0x00, 0x00,
132130803Smarcel  RETCODE_INSN5, 0x80,
133130803Smarcel};
134130803Smarcel
135130803Smarcelstatic LONGEST
136130803Smarceli386nbsd_sigtramp_offset (CORE_ADDR pc)
137130803Smarcel{
138130803Smarcel  unsigned char ret[sizeof(sigtramp_retcode)], insn;
139130803Smarcel  LONGEST off;
140130803Smarcel  int i;
141130803Smarcel
142130803Smarcel  if (read_memory_nobpt (pc, &insn, 1) != 0)
143130803Smarcel    return -1;
144130803Smarcel
145130803Smarcel  switch (insn)
146130803Smarcel    {
147130803Smarcel    case RETCODE_INSN1:
148130803Smarcel      off = 0;
149130803Smarcel      break;
150130803Smarcel
151130803Smarcel    case RETCODE_INSN2:
152130803Smarcel      /* INSN2 and INSN3 are the same.  Read at the location of PC+1
153130803Smarcel	 to determine if we're actually looking at INSN2 or INSN3.  */
154130803Smarcel      if (read_memory_nobpt (pc + 1, &insn, 1) != 0)
155130803Smarcel	return -1;
156130803Smarcel
157130803Smarcel      if (insn == RETCODE_INSN3)
158130803Smarcel	off = RETCODE_INSN2_OFF;
159130803Smarcel      else
160130803Smarcel	off = RETCODE_INSN3_OFF;
161130803Smarcel      break;
162130803Smarcel
163130803Smarcel    case RETCODE_INSN4:
164130803Smarcel      off = RETCODE_INSN4_OFF;
165130803Smarcel      break;
166130803Smarcel
167130803Smarcel    case RETCODE_INSN5:
168130803Smarcel      off = RETCODE_INSN5_OFF;
169130803Smarcel      break;
170130803Smarcel
171130803Smarcel    default:
172130803Smarcel      return -1;
173130803Smarcel    }
174130803Smarcel
175130803Smarcel  pc -= off;
176130803Smarcel
177130803Smarcel  if (read_memory_nobpt (pc, (char *) ret, sizeof (ret)) != 0)
178130803Smarcel    return -1;
179130803Smarcel
180130803Smarcel  if (memcmp (ret, sigtramp_retcode, sizeof (ret)) == 0)
181130803Smarcel    return off;
182130803Smarcel
183130803Smarcel  return -1;
184130803Smarcel}
185130803Smarcel
186130803Smarcelstatic int
187130803Smarceli386nbsd_pc_in_sigtramp (CORE_ADDR pc, char *name)
188130803Smarcel{
189130803Smarcel  return (nbsd_pc_in_sigtramp (pc, name)
190130803Smarcel	  || i386nbsd_sigtramp_offset (pc) >= 0);
191130803Smarcel}
192130803Smarcel
193130803Smarcel/* From <machine/signal.h>.  */
194130803Smarcelint i386nbsd_sc_reg_offset[] =
195130803Smarcel{
196130803Smarcel  10 * 4,			/* %eax */
197130803Smarcel  9 * 4,			/* %ecx */
198130803Smarcel  8 * 4,			/* %edx */
199130803Smarcel  7 * 4,			/* %ebx */
200130803Smarcel  14 * 4,			/* %esp */
201130803Smarcel  6 * 4,			/* %ebp */
202130803Smarcel  5 * 4,			/* %esi */
203130803Smarcel  4 * 4,			/* %edi */
204130803Smarcel  11 * 4,			/* %eip */
205130803Smarcel  13 * 4,			/* %eflags */
206130803Smarcel  12 * 4,			/* %cs */
207130803Smarcel  15 * 4,			/* %ss */
208130803Smarcel  3 * 4,			/* %ds */
209130803Smarcel  2 * 4,			/* %es */
210130803Smarcel  1 * 4,			/* %fs */
211130803Smarcel  0 * 4				/* %gs */
212130803Smarcel};
213130803Smarcel
214130803Smarcelstatic void
215130803Smarceli386nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
216130803Smarcel{
217130803Smarcel  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
218130803Smarcel
219130803Smarcel  /* Obviously NetBSD is BSD-based.  */
220130803Smarcel  i386bsd_init_abi (info, gdbarch);
221130803Smarcel
222130803Smarcel  /* NetBSD has a different `struct reg'.  */
223130803Smarcel  tdep->gregset_reg_offset = i386nbsd_r_reg_offset;
224130803Smarcel  tdep->gregset_num_regs = ARRAY_SIZE (i386nbsd_r_reg_offset);
225130803Smarcel  tdep->sizeof_gregset = 16 * 4;
226130803Smarcel
227130803Smarcel  /* NetBSD has different signal trampoline conventions.  */
228130803Smarcel  set_gdbarch_pc_in_sigtramp (gdbarch, i386nbsd_pc_in_sigtramp);
229130803Smarcel  /* FIXME: kettenis/20020906: We should probably provide
230130803Smarcel     NetBSD-specific versions of these functions if we want to
231130803Smarcel     recognize signal trampolines that live on the stack.  */
232130803Smarcel  set_gdbarch_sigtramp_start (gdbarch, NULL);
233130803Smarcel  set_gdbarch_sigtramp_end (gdbarch, NULL);
234130803Smarcel
235130803Smarcel  /* NetBSD uses -freg-struct-return by default.  */
236130803Smarcel  tdep->struct_return = reg_struct_return;
237130803Smarcel
238130803Smarcel  /* NetBSD has a `struct sigcontext' that's different from the
239130803Smarcel     origional 4.3 BSD.  */
240130803Smarcel  tdep->sc_reg_offset = i386nbsd_sc_reg_offset;
241130803Smarcel  tdep->sc_num_regs = ARRAY_SIZE (i386nbsd_sc_reg_offset);
242130803Smarcel}
243130803Smarcel
244130803Smarcel/* NetBSD a.out.  */
245130803Smarcel
246130803Smarcelstatic void
247130803Smarceli386nbsdaout_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
248130803Smarcel{
249130803Smarcel  i386nbsd_init_abi (info, gdbarch);
250130803Smarcel
251130803Smarcel  /* NetBSD a.out has a single register set.  */
252130803Smarcel  set_gdbarch_regset_from_core_section
253130803Smarcel    (gdbarch, i386nbsd_aout_regset_from_core_section);
254130803Smarcel}
255130803Smarcel
256130803Smarcel/* NetBSD ELF.  */
257130803Smarcel
258130803Smarcelstatic void
259130803Smarceli386nbsdelf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
260130803Smarcel{
261130803Smarcel  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
262130803Smarcel
263130803Smarcel  /* It's still NetBSD.  */
264130803Smarcel  i386nbsd_init_abi (info, gdbarch);
265130803Smarcel
266130803Smarcel  /* But ELF-based.  */
267130803Smarcel  i386_elf_init_abi (info, gdbarch);
268130803Smarcel
269130803Smarcel  /* NetBSD ELF uses SVR4-style shared libraries.  */
270130803Smarcel  set_gdbarch_in_solib_call_trampoline
271130803Smarcel    (gdbarch, generic_in_solib_call_trampoline);
272130803Smarcel  set_solib_svr4_fetch_link_map_offsets
273130803Smarcel    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
274130803Smarcel
275130803Smarcel  /* NetBSD ELF uses -fpcc-struct-return by default.  */
276130803Smarcel  tdep->struct_return = pcc_struct_return;
277130803Smarcel}
278130803Smarcel
279130803Smarcelvoid
280130803Smarcel_initialize_i386nbsd_tdep (void)
281130803Smarcel{
282130803Smarcel  gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_AOUT,
283130803Smarcel			  i386nbsdaout_init_abi);
284130803Smarcel  gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_ELF,
285130803Smarcel			  i386nbsdelf_init_abi);
286130803Smarcel}
287