1/* Target-dependent code for FreeBSD/i386.
2
3   Copyright (C) 2003, 2004, 2005, 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 "gdbcore.h"
23#include "osabi.h"
24#include "regcache.h"
25
26#include "gdb_assert.h"
27
28#include "i386-tdep.h"
29#include "i387-tdep.h"
30#include "bsd-uthread.h"
31#include "solib-svr4.h"
32
33/* FreeBSD 3.0-RELEASE or later.  */
34
35/* From <machine/reg.h>.  */
36static int i386fbsd_r_reg_offset[] =
37{
38  9 * 4, 8 * 4, 7 * 4, 6 * 4,	/* %eax, %ecx, %edx, %ebx */
39  15 * 4, 4 * 4,		/* %esp, %ebp */
40  3 * 4, 2 * 4,			/* %esi, %edi */
41  12 * 4, 14 * 4,		/* %eip, %eflags */
42  13 * 4, 16 * 4,		/* %cs, %ss */
43  1 * 4, 0 * 4, -1, -1		/* %ds, %es, %fs, %gs */
44};
45
46/* Sigtramp routine location.  */
47CORE_ADDR i386fbsd_sigtramp_start_addr = 0xbfbfdf20;
48CORE_ADDR i386fbsd_sigtramp_end_addr = 0xbfbfdff0;
49
50/* From <machine/signal.h>.  */
51int i386fbsd_sc_reg_offset[] =
52{
53  8 + 14 * 4,			/* %eax */
54  8 + 13 * 4,			/* %ecx */
55  8 + 12 * 4,			/* %edx */
56  8 + 11 * 4,			/* %ebx */
57  8 + 0 * 4,                    /* %esp */
58  8 + 1 * 4,                    /* %ebp */
59  8 + 10 * 4,                   /* %esi */
60  8 + 9 * 4,                    /* %edi */
61  8 + 3 * 4,                    /* %eip */
62  8 + 4 * 4,                    /* %eflags */
63  8 + 7 * 4,                    /* %cs */
64  8 + 8 * 4,                    /* %ss */
65  8 + 6 * 4,                    /* %ds */
66  8 + 5 * 4,                    /* %es */
67  8 + 15 * 4,			/* %fs */
68  8 + 16 * 4			/* %gs */
69};
70
71/* From /usr/src/lib/libc/i386/gen/_setjmp.S.  */
72static int i386fbsd_jmp_buf_reg_offset[] =
73{
74  -1,				/* %eax */
75  -1,				/* %ecx */
76  -1,				/* %edx */
77  1 * 4,			/* %ebx */
78  2 * 4,			/* %esp */
79  3 * 4,			/* %ebp */
80  4 * 4,			/* %esi */
81  5 * 4,			/* %edi */
82  0 * 4				/* %eip */
83};
84
85static void
86i386fbsd_supply_uthread (struct regcache *regcache,
87			 int regnum, CORE_ADDR addr)
88{
89  char buf[4];
90  int i;
91
92  gdb_assert (regnum >= -1);
93
94  for (i = 0; i < ARRAY_SIZE (i386fbsd_jmp_buf_reg_offset); i++)
95    {
96      if (i386fbsd_jmp_buf_reg_offset[i] != -1
97	  && (regnum == -1 || regnum == i))
98	{
99	  read_memory (addr + i386fbsd_jmp_buf_reg_offset[i], buf, 4);
100	  regcache_raw_supply (regcache, i, buf);
101	}
102    }
103}
104
105static void
106i386fbsd_collect_uthread (const struct regcache *regcache,
107			  int regnum, CORE_ADDR addr)
108{
109  char buf[4];
110  int i;
111
112  gdb_assert (regnum >= -1);
113
114  for (i = 0; i < ARRAY_SIZE (i386fbsd_jmp_buf_reg_offset); i++)
115    {
116      if (i386fbsd_jmp_buf_reg_offset[i] != -1
117	  && (regnum == -1 || regnum == i))
118	{
119	  regcache_raw_collect (regcache, i, buf);
120	  write_memory (addr + i386fbsd_jmp_buf_reg_offset[i], buf, 4);
121	}
122    }
123}
124
125static void
126i386fbsdaout_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
127{
128  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
129
130  /* Obviously FreeBSD is BSD-based.  */
131  i386bsd_init_abi (info, gdbarch);
132
133  /* FreeBSD has a different `struct reg', and reserves some space for
134     its FPU emulator in `struct fpreg'.  */
135  tdep->gregset_reg_offset = i386fbsd_r_reg_offset;
136  tdep->gregset_num_regs = ARRAY_SIZE (i386fbsd_r_reg_offset);
137  tdep->sizeof_gregset = 18 * 4;
138  tdep->sizeof_fpregset = 176;
139
140  /* FreeBSD uses -freg-struct-return by default.  */
141  tdep->struct_return = reg_struct_return;
142
143  /* FreeBSD uses a different memory layout.  */
144  tdep->sigtramp_start = i386fbsd_sigtramp_start_addr;
145  tdep->sigtramp_end = i386fbsd_sigtramp_end_addr;
146
147  /* FreeBSD has a more complete `struct sigcontext'.  */
148  tdep->sc_reg_offset = i386fbsd_sc_reg_offset;
149  tdep->sc_num_regs = ARRAY_SIZE (i386fbsd_sc_reg_offset);
150
151  /* FreeBSD provides a user-level threads implementation.  */
152  bsd_uthread_set_supply_uthread (gdbarch, i386fbsd_supply_uthread);
153  bsd_uthread_set_collect_uthread (gdbarch, i386fbsd_collect_uthread);
154}
155
156static void
157i386fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
158{
159  /* It's almost identical to FreeBSD a.out.  */
160  i386fbsdaout_init_abi (info, gdbarch);
161
162  /* Except that it uses ELF.  */
163  i386_elf_init_abi (info, gdbarch);
164
165  /* FreeBSD ELF uses SVR4-style shared libraries.  */
166  set_solib_svr4_fetch_link_map_offsets
167    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
168}
169
170/* FreeBSD 4.0-RELEASE or later.  */
171
172/* From <machine/reg.h>.  */
173static int i386fbsd4_r_reg_offset[] =
174{
175  10 * 4, 9 * 4, 8 * 4, 7 * 4,	/* %eax, %ecx, %edx, %ebx */
176  16 * 4, 5 * 4,		/* %esp, %ebp */
177  4 * 4, 3 * 4,			/* %esi, %edi */
178  13 * 4, 15 * 4,		/* %eip, %eflags */
179  14 * 4, 17 * 4,		/* %cs, %ss */
180  2 * 4, 1 * 4, 0 * 4, 18 * 4	/* %ds, %es, %fs, %gs */
181};
182
183/* From <machine/signal.h>.  */
184int i386fbsd4_sc_reg_offset[] =
185{
186  20 + 11 * 4,			/* %eax */
187  20 + 10 * 4,			/* %ecx */
188  20 + 9 * 4,			/* %edx */
189  20 + 8 * 4,			/* %ebx */
190  20 + 17 * 4,			/* %esp */
191  20 + 6 * 4,			/* %ebp */
192  20 + 5 * 4,			/* %esi */
193  20 + 4 * 4,			/* %edi */
194  20 + 14 * 4,			/* %eip */
195  20 + 16 * 4,			/* %eflags */
196  20 + 15 * 4,			/* %cs */
197  20 + 18 * 4,			/* %ss */
198  20 + 3 * 4,			/* %ds */
199  20 + 2 * 4,			/* %es */
200  20 + 1 * 4,			/* %fs */
201  20 + 0 * 4			/* %gs */
202};
203
204static void
205i386fbsd4_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
206{
207  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
208
209  /* Inherit stuff from older releases.  We assume that FreeBSD
210     4.0-RELEASE always uses ELF.  */
211  i386fbsd_init_abi (info, gdbarch);
212
213  /* FreeBSD 4.0 introduced a new `struct reg'.  */
214  tdep->gregset_reg_offset = i386fbsd4_r_reg_offset;
215  tdep->gregset_num_regs = ARRAY_SIZE (i386fbsd4_r_reg_offset);
216  tdep->sizeof_gregset = 19 * 4;
217
218  /* FreeBSD 4.0 introduced a new `struct sigcontext'.  */
219  tdep->sc_reg_offset = i386fbsd4_sc_reg_offset;
220  tdep->sc_num_regs = ARRAY_SIZE (i386fbsd4_sc_reg_offset);
221}
222
223
224/* Provide a prototype to silence -Wmissing-prototypes.  */
225void _initialize_i386fbsd_tdep (void);
226
227void
228_initialize_i386fbsd_tdep (void)
229{
230  gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_FREEBSD_AOUT,
231			  i386fbsdaout_init_abi);
232  gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_FREEBSD_ELF,
233			  i386fbsd4_init_abi);
234}
235