Dwarf assembler: handle one instruction function
[deliverable/binutils-gdb.git] / gdb / i386fbsd-tdep.c
1 /* Target-dependent code for FreeBSD/i386.
2
3 Copyright (C) 2003-2015 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 "i386-tdep.h"
27 #include "i387-tdep.h"
28 #include "bsd-uthread.h"
29 #include "fbsd-tdep.h"
30 #include "solib-svr4.h"
31
32 /* Support for signal handlers. */
33
34 /* Return whether THIS_FRAME corresponds to a FreeBSD sigtramp
35 routine. */
36
37 /* FreeBSD/i386 supports three different signal trampolines, one for
38 versions before 4.0, a second for 4.x, and a third for 5.0 and
39 later. To complicate matters, FreeBSD/i386 binaries running under
40 an amd64 kernel use a different set of trampolines. These
41 trampolines differ from the i386 kernel trampolines in that they
42 omit a middle section that conditionally restores %gs. */
43
44 static const gdb_byte i386fbsd_sigtramp_start[] =
45 {
46 0x8d, 0x44, 0x24, 0x20, /* lea SIGF_UC(%esp),%eax */
47 0x50 /* pushl %eax */
48 };
49
50 static const gdb_byte i386fbsd_sigtramp_middle[] =
51 {
52 0xf7, 0x40, 0x54, 0x00, 0x00, 0x02, 0x00,
53 /* testl $PSL_VM,UC_EFLAGS(%eax) */
54 0x75, 0x03, /* jne +3 */
55 0x8e, 0x68, 0x14 /* mov UC_GS(%eax),%gs */
56 };
57
58 static const gdb_byte i386fbsd_sigtramp_end[] =
59 {
60 0xb8, 0xa1, 0x01, 0x00, 0x00, /* movl $SYS_sigreturn,%eax */
61 0x50, /* pushl %eax */
62 0xcd, 0x80 /* int $0x80 */
63 };
64
65 static const gdb_byte i386fbsd_freebsd4_sigtramp_start[] =
66 {
67 0x8d, 0x44, 0x24, 0x14, /* lea SIGF_UC4(%esp),%eax */
68 0x50 /* pushl %eax */
69 };
70
71 static const gdb_byte i386fbsd_freebsd4_sigtramp_middle[] =
72 {
73 0xf7, 0x40, 0x54, 0x00, 0x00, 0x02, 0x00,
74 /* testl $PSL_VM,UC4_EFLAGS(%eax) */
75 0x75, 0x03, /* jne +3 */
76 0x8e, 0x68, 0x14 /* mov UC4_GS(%eax),%gs */
77 };
78
79 static const gdb_byte i386fbsd_freebsd4_sigtramp_end[] =
80 {
81 0xb8, 0x58, 0x01, 0x00, 0x00, /* movl $344,%eax */
82 0x50, /* pushl %eax */
83 0xcd, 0x80 /* int $0x80 */
84 };
85
86 static const gdb_byte i386fbsd_osigtramp_start[] =
87 {
88 0x8d, 0x44, 0x24, 0x14, /* lea SIGF_SC(%esp),%eax */
89 0x50 /* pushl %eax */
90 };
91
92 static const gdb_byte i386fbsd_osigtramp_middle[] =
93 {
94 0xf7, 0x40, 0x18, 0x00, 0x00, 0x02, 0x00,
95 /* testl $PSL_VM,SC_PS(%eax) */
96 0x75, 0x03, /* jne +3 */
97 0x8e, 0x68, 0x44 /* mov SC_GS(%eax),%gs */
98 };
99
100 static const gdb_byte i386fbsd_osigtramp_end[] =
101 {
102 0xb8, 0x67, 0x00, 0x00, 0x00, /* movl $103,%eax */
103 0x50, /* pushl %eax */
104 0xcd, 0x80 /* int $0x80 */
105 };
106
107 /* The three different trampolines are all the same size. */
108 gdb_static_assert (sizeof i386fbsd_sigtramp_start ==
109 sizeof i386fbsd_freebsd4_sigtramp_start);
110 gdb_static_assert (sizeof i386fbsd_sigtramp_start ==
111 sizeof i386fbsd_osigtramp_start);
112 gdb_static_assert (sizeof i386fbsd_sigtramp_middle ==
113 sizeof i386fbsd_freebsd4_sigtramp_middle);
114 gdb_static_assert (sizeof i386fbsd_sigtramp_middle ==
115 sizeof i386fbsd_osigtramp_middle);
116 gdb_static_assert (sizeof i386fbsd_sigtramp_end ==
117 sizeof i386fbsd_freebsd4_sigtramp_end);
118 gdb_static_assert (sizeof i386fbsd_sigtramp_end ==
119 sizeof i386fbsd_osigtramp_end);
120
121 /* We assume that the middle is the largest chunk below. */
122 gdb_static_assert (sizeof i386fbsd_sigtramp_middle >
123 sizeof i386fbsd_sigtramp_start);
124 gdb_static_assert (sizeof i386fbsd_sigtramp_middle >
125 sizeof i386fbsd_sigtramp_end);
126
127 static int
128 i386fbsd_sigtramp_p (struct frame_info *this_frame)
129 {
130 CORE_ADDR pc = get_frame_pc (this_frame);
131 gdb_byte buf[sizeof i386fbsd_sigtramp_middle];
132 const gdb_byte *middle, *end;
133
134 /* Look for a matching start. */
135 if (!safe_frame_unwind_memory (this_frame, pc, buf,
136 sizeof i386fbsd_sigtramp_start))
137 return 0;
138 if (memcmp (buf, i386fbsd_sigtramp_start, sizeof i386fbsd_sigtramp_start) ==
139 0) {
140 middle = i386fbsd_sigtramp_middle;
141 end = i386fbsd_sigtramp_end;
142 } else if (memcmp (buf, i386fbsd_freebsd4_sigtramp_start,
143 sizeof i386fbsd_freebsd4_sigtramp_start) == 0) {
144 middle = i386fbsd_freebsd4_sigtramp_middle;
145 end = i386fbsd_freebsd4_sigtramp_end;
146 } else if (memcmp (buf, i386fbsd_osigtramp_start,
147 sizeof i386fbsd_osigtramp_start) == 0) {
148 middle = i386fbsd_osigtramp_middle;
149 end = i386fbsd_osigtramp_end;
150 } else
151 return 0;
152
153 /* Since the end is shorter than the middle, check for a matching end
154 next. */
155 pc += sizeof i386fbsd_sigtramp_start;
156 if (!safe_frame_unwind_memory (this_frame, pc, buf,
157 sizeof i386fbsd_sigtramp_end))
158 return 0;
159 if (memcmp (buf, end, sizeof i386fbsd_sigtramp_end) == 0)
160 return 1;
161
162 /* If the end didn't match, check for a matching middle. */
163 if (!safe_frame_unwind_memory (this_frame, pc, buf,
164 sizeof i386fbsd_sigtramp_middle))
165 return 0;
166 if (memcmp (buf, middle, sizeof i386fbsd_sigtramp_middle) != 0)
167 return 0;
168
169 /* The middle matched, check for a matching end. */
170 pc += sizeof i386fbsd_sigtramp_middle;
171 if (!safe_frame_unwind_memory (this_frame, pc, buf,
172 sizeof i386fbsd_sigtramp_end))
173 return 0;
174 if (memcmp (buf, end, sizeof i386fbsd_sigtramp_end) != 0)
175 return 0;
176
177 return 1;
178 }
179
180 /* FreeBSD 3.0-RELEASE or later. */
181
182 /* From <machine/reg.h>. */
183 static int i386fbsd_r_reg_offset[] =
184 {
185 9 * 4, 8 * 4, 7 * 4, 6 * 4, /* %eax, %ecx, %edx, %ebx */
186 15 * 4, 4 * 4, /* %esp, %ebp */
187 3 * 4, 2 * 4, /* %esi, %edi */
188 12 * 4, 14 * 4, /* %eip, %eflags */
189 13 * 4, 16 * 4, /* %cs, %ss */
190 1 * 4, 0 * 4, -1, -1 /* %ds, %es, %fs, %gs */
191 };
192
193 /* Sigtramp routine location. */
194 CORE_ADDR i386fbsd_sigtramp_start_addr;
195 CORE_ADDR i386fbsd_sigtramp_end_addr;
196
197 /* From <machine/signal.h>. */
198 int i386fbsd_sc_reg_offset[] =
199 {
200 8 + 14 * 4, /* %eax */
201 8 + 13 * 4, /* %ecx */
202 8 + 12 * 4, /* %edx */
203 8 + 11 * 4, /* %ebx */
204 8 + 0 * 4, /* %esp */
205 8 + 1 * 4, /* %ebp */
206 8 + 10 * 4, /* %esi */
207 8 + 9 * 4, /* %edi */
208 8 + 3 * 4, /* %eip */
209 8 + 4 * 4, /* %eflags */
210 8 + 7 * 4, /* %cs */
211 8 + 8 * 4, /* %ss */
212 8 + 6 * 4, /* %ds */
213 8 + 5 * 4, /* %es */
214 8 + 15 * 4, /* %fs */
215 8 + 16 * 4 /* %gs */
216 };
217
218 /* From /usr/src/lib/libc/i386/gen/_setjmp.S. */
219 static int i386fbsd_jmp_buf_reg_offset[] =
220 {
221 -1, /* %eax */
222 -1, /* %ecx */
223 -1, /* %edx */
224 1 * 4, /* %ebx */
225 2 * 4, /* %esp */
226 3 * 4, /* %ebp */
227 4 * 4, /* %esi */
228 5 * 4, /* %edi */
229 0 * 4 /* %eip */
230 };
231
232 static void
233 i386fbsd_supply_uthread (struct regcache *regcache,
234 int regnum, CORE_ADDR addr)
235 {
236 gdb_byte buf[4];
237 int i;
238
239 gdb_assert (regnum >= -1);
240
241 for (i = 0; i < ARRAY_SIZE (i386fbsd_jmp_buf_reg_offset); i++)
242 {
243 if (i386fbsd_jmp_buf_reg_offset[i] != -1
244 && (regnum == -1 || regnum == i))
245 {
246 read_memory (addr + i386fbsd_jmp_buf_reg_offset[i], buf, 4);
247 regcache_raw_supply (regcache, i, buf);
248 }
249 }
250 }
251
252 static void
253 i386fbsd_collect_uthread (const struct regcache *regcache,
254 int regnum, CORE_ADDR addr)
255 {
256 gdb_byte buf[4];
257 int i;
258
259 gdb_assert (regnum >= -1);
260
261 for (i = 0; i < ARRAY_SIZE (i386fbsd_jmp_buf_reg_offset); i++)
262 {
263 if (i386fbsd_jmp_buf_reg_offset[i] != -1
264 && (regnum == -1 || regnum == i))
265 {
266 regcache_raw_collect (regcache, i, buf);
267 write_memory (addr + i386fbsd_jmp_buf_reg_offset[i], buf, 4);
268 }
269 }
270 }
271
272 static void
273 i386fbsdaout_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
274 {
275 struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
276
277 /* Obviously FreeBSD is BSD-based. */
278 i386bsd_init_abi (info, gdbarch);
279
280 /* FreeBSD has a different `struct reg', and reserves some space for
281 its FPU emulator in `struct fpreg'. */
282 tdep->gregset_reg_offset = i386fbsd_r_reg_offset;
283 tdep->gregset_num_regs = ARRAY_SIZE (i386fbsd_r_reg_offset);
284 tdep->sizeof_gregset = 18 * 4;
285 tdep->sizeof_fpregset = 176;
286
287 /* FreeBSD uses -freg-struct-return by default. */
288 tdep->struct_return = reg_struct_return;
289
290 tdep->sigtramp_p = i386fbsd_sigtramp_p;
291
292 /* FreeBSD uses a different memory layout. */
293 tdep->sigtramp_start = i386fbsd_sigtramp_start_addr;
294 tdep->sigtramp_end = i386fbsd_sigtramp_end_addr;
295
296 /* FreeBSD has a more complete `struct sigcontext'. */
297 tdep->sc_reg_offset = i386fbsd_sc_reg_offset;
298 tdep->sc_num_regs = ARRAY_SIZE (i386fbsd_sc_reg_offset);
299
300 /* FreeBSD provides a user-level threads implementation. */
301 bsd_uthread_set_supply_uthread (gdbarch, i386fbsd_supply_uthread);
302 bsd_uthread_set_collect_uthread (gdbarch, i386fbsd_collect_uthread);
303 }
304
305 static void
306 i386fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
307 {
308 /* It's almost identical to FreeBSD a.out. */
309 i386fbsdaout_init_abi (info, gdbarch);
310
311 /* Except that it uses ELF. */
312 i386_elf_init_abi (info, gdbarch);
313
314 /* FreeBSD ELF uses SVR4-style shared libraries. */
315 set_solib_svr4_fetch_link_map_offsets
316 (gdbarch, svr4_ilp32_fetch_link_map_offsets);
317 }
318
319 /* FreeBSD 4.0-RELEASE or later. */
320
321 /* From <machine/reg.h>. */
322 static int i386fbsd4_r_reg_offset[] =
323 {
324 10 * 4, 9 * 4, 8 * 4, 7 * 4, /* %eax, %ecx, %edx, %ebx */
325 16 * 4, 5 * 4, /* %esp, %ebp */
326 4 * 4, 3 * 4, /* %esi, %edi */
327 13 * 4, 15 * 4, /* %eip, %eflags */
328 14 * 4, 17 * 4, /* %cs, %ss */
329 2 * 4, 1 * 4, 0 * 4, 18 * 4 /* %ds, %es, %fs, %gs */
330 };
331
332 /* From <machine/signal.h>. */
333 int i386fbsd4_sc_reg_offset[] =
334 {
335 20 + 11 * 4, /* %eax */
336 20 + 10 * 4, /* %ecx */
337 20 + 9 * 4, /* %edx */
338 20 + 8 * 4, /* %ebx */
339 20 + 17 * 4, /* %esp */
340 20 + 6 * 4, /* %ebp */
341 20 + 5 * 4, /* %esi */
342 20 + 4 * 4, /* %edi */
343 20 + 14 * 4, /* %eip */
344 20 + 16 * 4, /* %eflags */
345 20 + 15 * 4, /* %cs */
346 20 + 18 * 4, /* %ss */
347 20 + 3 * 4, /* %ds */
348 20 + 2 * 4, /* %es */
349 20 + 1 * 4, /* %fs */
350 20 + 0 * 4 /* %gs */
351 };
352
353 static void
354 i386fbsd4_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
355 {
356 struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
357
358 /* Generic FreeBSD support. */
359 fbsd_init_abi (info, gdbarch);
360
361 /* Inherit stuff from older releases. We assume that FreeBSD
362 4.0-RELEASE always uses ELF. */
363 i386fbsd_init_abi (info, gdbarch);
364
365 /* FreeBSD 4.0 introduced a new `struct reg'. */
366 tdep->gregset_reg_offset = i386fbsd4_r_reg_offset;
367 tdep->gregset_num_regs = ARRAY_SIZE (i386fbsd4_r_reg_offset);
368 tdep->sizeof_gregset = 19 * 4;
369
370 /* FreeBSD 4.0 introduced a new `struct sigcontext'. */
371 tdep->sc_reg_offset = i386fbsd4_sc_reg_offset;
372 tdep->sc_num_regs = ARRAY_SIZE (i386fbsd4_sc_reg_offset);
373 }
374
375 \f
376 /* Provide a prototype to silence -Wmissing-prototypes. */
377 void _initialize_i386fbsd_tdep (void);
378
379 void
380 _initialize_i386fbsd_tdep (void)
381 {
382 gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_FREEBSD_AOUT,
383 i386fbsdaout_init_abi);
384 gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_FREEBSD_ELF,
385 i386fbsd4_init_abi);
386 }
This page took 0.037833 seconds and 4 git commands to generate.