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 "gdbcore.h"
27 #include "regcache.h"
28 #include "regset.h"
29 #include "osabi.h"
30 
31 #include "gdb_assert.h"
32 #include "gdb_string.h"
33 
34 #include "i386-tdep.h"
35 #include "i387-tdep.h"
36 #include "nbsd-tdep.h"
37 #include "solib-svr4.h"
38 
39 /* From <machine/reg.h>.  */
40 static int i386nbsd_r_reg_offset[] =
41 {
42   0 * 4,			/* %eax */
43   1 * 4,			/* %ecx */
44   2 * 4,			/* %edx */
45   3 * 4,			/* %ebx */
46   4 * 4,			/* %esp */
47   5 * 4,			/* %ebp */
48   6 * 4,			/* %esi */
49   7 * 4,			/* %edi */
50   8 * 4,			/* %eip */
51   9 * 4,			/* %eflags */
52   10 * 4,			/* %cs */
53   11 * 4,			/* %ss */
54   12 * 4,			/* %ds */
55   13 * 4,			/* %es */
56   14 * 4,			/* %fs */
57   15 * 4			/* %gs */
58 };
59 
60 static void
i386nbsd_aout_supply_regset(const struct regset * regset,struct regcache * regcache,int regnum,const void * regs,size_t len)61 i386nbsd_aout_supply_regset (const struct regset *regset,
62 			     struct regcache *regcache, int regnum,
63 			     const void *regs, size_t len)
64 {
65   const struct gdbarch_tdep *tdep = regset->descr;
66 
67   gdb_assert (len >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE);
68 
69   i386_supply_gregset (regset, regcache, regnum, regs, tdep->sizeof_gregset);
70   i387_supply_fsave (regcache, regnum, (char *) regs + tdep->sizeof_gregset);
71 }
72 
73 static const struct regset *
i386nbsd_aout_regset_from_core_section(struct gdbarch * gdbarch,const char * sect_name,size_t sect_size)74 i386nbsd_aout_regset_from_core_section (struct gdbarch *gdbarch,
75 					const char *sect_name,
76 					size_t sect_size)
77 {
78   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
79 
80   /* NetBSD a.out core dumps don't use seperate register sets for the
81      general-purpose and floating-point registers.  */
82 
83   if (strcmp (sect_name, ".reg") == 0
84       && sect_size >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE)
85     {
86       if (tdep->gregset == NULL)
87 	{
88 	  tdep->gregset = XMALLOC (struct regset);
89 	  tdep->gregset->descr = tdep;
90 	  tdep->gregset->supply_regset = i386nbsd_aout_supply_regset;
91 	}
92       return tdep->gregset;
93     }
94 
95   return NULL;
96 }
97 
98 /* Under NetBSD/i386, signal handler invocations can be identified by the
99    designated code sequence that is used to return from a signal handler.
100    In particular, the return address of a signal handler points to the
101    following code sequence:
102 
103 	leal	0x10(%esp), %eax
104 	pushl	%eax
105 	pushl	%eax
106 	movl	$0x127, %eax		# __sigreturn14
107 	int	$0x80
108 
109    Each instruction has a unique encoding, so we simply attempt to match
110    the instruction the PC is pointing to with any of the above instructions.
111    If there is a hit, we know the offset to the start of the designated
112    sequence and can then check whether we really are executing in the
113    signal trampoline.  If not, -1 is returned, otherwise the offset from the
114    start of the return sequence is returned.  */
115 #define RETCODE_INSN1		0x8d
116 #define RETCODE_INSN2		0x50
117 #define RETCODE_INSN3		0x50
118 #define RETCODE_INSN4		0xb8
119 #define RETCODE_INSN5		0xcd
120 
121 #define RETCODE_INSN2_OFF	4
122 #define RETCODE_INSN3_OFF	5
123 #define RETCODE_INSN4_OFF	6
124 #define RETCODE_INSN5_OFF	11
125 
126 static const unsigned char sigtramp_retcode[] =
127 {
128   RETCODE_INSN1, 0x44, 0x24, 0x10,
129   RETCODE_INSN2,
130   RETCODE_INSN3,
131   RETCODE_INSN4, 0x27, 0x01, 0x00, 0x00,
132   RETCODE_INSN5, 0x80,
133 };
134 
135 static LONGEST
i386nbsd_sigtramp_offset(CORE_ADDR pc)136 i386nbsd_sigtramp_offset (CORE_ADDR pc)
137 {
138   unsigned char ret[sizeof(sigtramp_retcode)], insn;
139   LONGEST off;
140   int i;
141 
142   if (read_memory_nobpt (pc, &insn, 1) != 0)
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 (read_memory_nobpt (pc + 1, &insn, 1) != 0)
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 (read_memory_nobpt (pc, (char *) ret, sizeof (ret)) != 0)
178     return -1;
179 
180   if (memcmp (ret, sigtramp_retcode, sizeof (ret)) == 0)
181     return off;
182 
183   return -1;
184 }
185 
186 static int
i386nbsd_pc_in_sigtramp(CORE_ADDR pc,char * name)187 i386nbsd_pc_in_sigtramp (CORE_ADDR pc, char *name)
188 {
189   return (nbsd_pc_in_sigtramp (pc, name)
190 	  || i386nbsd_sigtramp_offset (pc) >= 0);
191 }
192 
193 /* From <machine/signal.h>.  */
194 int i386nbsd_sc_reg_offset[] =
195 {
196   10 * 4,			/* %eax */
197   9 * 4,			/* %ecx */
198   8 * 4,			/* %edx */
199   7 * 4,			/* %ebx */
200   14 * 4,			/* %esp */
201   6 * 4,			/* %ebp */
202   5 * 4,			/* %esi */
203   4 * 4,			/* %edi */
204   11 * 4,			/* %eip */
205   13 * 4,			/* %eflags */
206   12 * 4,			/* %cs */
207   15 * 4,			/* %ss */
208   3 * 4,			/* %ds */
209   2 * 4,			/* %es */
210   1 * 4,			/* %fs */
211   0 * 4				/* %gs */
212 };
213 
214 static void
i386nbsd_init_abi(struct gdbarch_info info,struct gdbarch * gdbarch)215 i386nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
216 {
217   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
218 
219   /* Obviously NetBSD is BSD-based.  */
220   i386bsd_init_abi (info, gdbarch);
221 
222   /* NetBSD has a different `struct reg'.  */
223   tdep->gregset_reg_offset = i386nbsd_r_reg_offset;
224   tdep->gregset_num_regs = ARRAY_SIZE (i386nbsd_r_reg_offset);
225   tdep->sizeof_gregset = 16 * 4;
226 
227   /* NetBSD has different signal trampoline conventions.  */
228   set_gdbarch_pc_in_sigtramp (gdbarch, i386nbsd_pc_in_sigtramp);
229   /* FIXME: kettenis/20020906: We should probably provide
230      NetBSD-specific versions of these functions if we want to
231      recognize signal trampolines that live on the stack.  */
232   set_gdbarch_sigtramp_start (gdbarch, NULL);
233   set_gdbarch_sigtramp_end (gdbarch, NULL);
234 
235   /* NetBSD uses -freg-struct-return by default.  */
236   tdep->struct_return = reg_struct_return;
237 
238   /* NetBSD has a `struct sigcontext' that's different from the
239      origional 4.3 BSD.  */
240   tdep->sc_reg_offset = i386nbsd_sc_reg_offset;
241   tdep->sc_num_regs = ARRAY_SIZE (i386nbsd_sc_reg_offset);
242 }
243 
244 /* NetBSD a.out.  */
245 
246 static void
i386nbsdaout_init_abi(struct gdbarch_info info,struct gdbarch * gdbarch)247 i386nbsdaout_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
248 {
249   i386nbsd_init_abi (info, gdbarch);
250 
251   /* NetBSD a.out has a single register set.  */
252   set_gdbarch_regset_from_core_section
253     (gdbarch, i386nbsd_aout_regset_from_core_section);
254 }
255 
256 /* NetBSD ELF.  */
257 
258 static void
i386nbsdelf_init_abi(struct gdbarch_info info,struct gdbarch * gdbarch)259 i386nbsdelf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
260 {
261   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
262 
263   /* It's still NetBSD.  */
264   i386nbsd_init_abi (info, gdbarch);
265 
266   /* But ELF-based.  */
267   i386_elf_init_abi (info, gdbarch);
268 
269   /* NetBSD ELF uses SVR4-style shared libraries.  */
270   set_gdbarch_in_solib_call_trampoline
271     (gdbarch, generic_in_solib_call_trampoline);
272   set_solib_svr4_fetch_link_map_offsets
273     (gdbarch, svr4_ilp32_fetch_link_map_offsets);
274 
275   /* NetBSD ELF uses -fpcc-struct-return by default.  */
276   tdep->struct_return = pcc_struct_return;
277 }
278 
279 void
_initialize_i386nbsd_tdep(void)280 _initialize_i386nbsd_tdep (void)
281 {
282   gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_AOUT,
283 			  i386nbsdaout_init_abi);
284   gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_ELF,
285 			  i386nbsdelf_init_abi);
286 }
287