1/* Target-dependent code for OpenBSD/amd64.
2
3   Copyright 2003, 2004 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 2 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, write to the Free Software
19   Foundation, Inc., 59 Temple Place - Suite 330,
20   Boston, MA 02111-1307, USA.  */
21
22#include "defs.h"
23#include "frame.h"
24#include "gdbcore.h"
25#include "symtab.h"
26#include "objfiles.h"
27#include "osabi.h"
28#include "regset.h"
29#include "target.h"
30
31#include "gdb_assert.h"
32#include "gdb_string.h"
33
34#include "amd64-tdep.h"
35#include "i387-tdep.h"
36#include "solib-svr4.h"
37
38/* Support for core dumps.  */
39
40static void
41amd64obsd_supply_regset (const struct regset *regset,
42			 struct regcache *regcache, int regnum,
43			 const void *regs, size_t len)
44{
45  const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
46
47  gdb_assert (len >= tdep->sizeof_gregset + I387_SIZEOF_FXSAVE);
48
49  i386_supply_gregset (regset, regcache, regnum, regs, tdep->sizeof_gregset);
50  amd64_supply_fxsave (regcache, regnum, (char *)regs + tdep->sizeof_gregset);
51}
52
53static const struct regset *
54amd64obsd_regset_from_core_section (struct gdbarch *gdbarch,
55				    const char *sect_name, size_t sect_size)
56{
57  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
58
59  /* OpenBSD core dumps don't use seperate register sets for the
60     general-purpose and floating-point registers.  */
61
62  if (strcmp (sect_name, ".reg") == 0
63      && sect_size >= tdep->sizeof_gregset + I387_SIZEOF_FXSAVE)
64    {
65      if (tdep->gregset == NULL)
66        tdep->gregset = regset_alloc (gdbarch, amd64obsd_supply_regset, NULL);
67      return tdep->gregset;
68    }
69
70  return NULL;
71}
72
73
74/* Support for signal handlers.  */
75
76/* Default page size.  */
77static const int amd64obsd_page_size = 4096;
78
79/* Return whether the frame preceding NEXT_FRAME corresponds to an
80   OpenBSD sigtramp routine.  */
81
82static int
83amd64obsd_sigtramp_p (struct frame_info *next_frame)
84{
85  CORE_ADDR pc = frame_pc_unwind (next_frame);
86  CORE_ADDR start_pc = (pc & ~(amd64obsd_page_size - 1));
87  const char sigreturn[] =
88  {
89    0x48, 0xc7, 0xc0,
90    0x67, 0x00, 0x00, 0x00,	/* movq $SYS_sigreturn, %rax */
91    0xcd, 0x80			/* int $0x80 */
92  };
93  size_t buflen = (sizeof sigreturn) + 1;
94  char *name, *buf;
95
96  /* If the function has a valid symbol name, it isn't a
97     trampoline.  */
98  find_pc_partial_function (pc, &name, NULL, NULL);
99  if (name != NULL)
100    return 0;
101
102  /* If the function lives in a valid section (even without a starting
103     point) it isn't a trampoline.  */
104  if (find_pc_section (pc) != NULL)
105    return 0;
106
107  /* If we can't read the instructions at START_PC, return zero.  */
108  buf = alloca ((sizeof sigreturn) + 1);
109  if (!safe_frame_unwind_memory (next_frame, start_pc + 6, buf, buflen))
110    return 0;
111
112  /* Check for sigreturn(2).  Depending on how the assembler encoded
113     the `movq %rsp, %rdi' instruction, the code starts at offset 6 or
114     7.  */
115  if (memcmp (buf, sigreturn, sizeof sigreturn)
116      && memcpy (buf + 1, sigreturn, sizeof sigreturn))
117    return 0;
118
119  return 1;
120}
121
122/* Assuming NEXT_FRAME is for a frame following a BSD sigtramp
123   routine, return the address of the associated sigcontext structure.  */
124
125static CORE_ADDR
126amd64obsd_sigcontext_addr (struct frame_info *next_frame)
127{
128  CORE_ADDR pc = frame_pc_unwind (next_frame);
129  ULONGEST offset = (pc & (amd64obsd_page_size - 1));
130
131  /* The %rsp register points at `struct sigcontext' upon entry of a
132     signal trampoline.  The relevant part of the trampoline is
133
134        call    *%rax
135        movq    %rsp, %rdi
136        pushq   %rdi
137        movq    $SYS_sigreturn,%rax
138        int     $0x80
139
140     (see /usr/src/sys/arch/amd64/amd64/locore.S).  The `pushq'
141     instruction clobbers %rsp, but its value is saved in `%rdi'.  */
142
143  if (offset > 5)
144    return frame_unwind_register_unsigned (next_frame, AMD64_RDI_REGNUM);
145  else
146    return frame_unwind_register_unsigned (next_frame, AMD64_RSP_REGNUM);
147}
148
149/* OpenBSD 3.5 or later.  */
150
151/* Mapping between the general-purpose registers in `struct reg'
152   format and GDB's register cache layout.  */
153
154/* From <machine/reg.h>.  */
155int amd64obsd_r_reg_offset[] =
156{
157  14 * 8,			/* %rax */
158  13 * 8,			/* %rbx */
159  3 * 8,			/* %rcx */
160  2 * 8,			/* %rdx */
161  1 * 8,			/* %rsi */
162  0 * 8,			/* %rdi */
163  12 * 8,			/* %rbp */
164  15 * 8,			/* %rsp */
165  4 * 8,			/* %r8 .. */
166  5 * 8,
167  6 * 8,
168  7 * 8,
169  8 * 8,
170  9 * 8,
171  10 * 8,
172  11 * 8,			/* ... %r15 */
173  16 * 8,			/* %rip */
174  17 * 8,			/* %eflags */
175  18 * 8,			/* %cs */
176  19 * 8,			/* %ss */
177  20 * 8,			/* %ds */
178  21 * 8,			/* %es */
179  22 * 8,			/* %fs */
180  23 * 8			/* %gs */
181};
182
183/* From <machine/signal.h>.  */
184static int amd64obsd_sc_reg_offset[] =
185{
186  14 * 8,			/* %rax */
187  13 * 8,			/* %rbx */
188  3 * 8,			/* %rcx */
189  2 * 8,			/* %rdx */
190  1 * 8,			/* %rsi */
191  0 * 8,			/* %rdi */
192  12 * 8,			/* %rbp */
193  24 * 8,			/* %rsp */
194  4 * 8,			/* %r8 ... */
195  5 * 8,
196  6 * 8,
197  7 * 8,
198  8 * 8,
199  9 * 8,
200  10 * 8,
201  11 * 8,			/* ... %r15 */
202  21 * 8,			/* %rip */
203  23 * 8,			/* %eflags */
204  22 * 8,			/* %cs */
205  25 * 8,			/* %ss */
206  18 * 8,			/* %ds */
207  17 * 8,			/* %es */
208  16 * 8,			/* %fs */
209  15 * 8			/* %gs */
210};
211
212static void
213amd64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
214{
215  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
216
217  amd64_init_abi (info, gdbarch);
218
219  /* Initialize general-purpose register set details.  */
220  tdep->gregset_reg_offset = amd64obsd_r_reg_offset;
221  tdep->gregset_num_regs = ARRAY_SIZE (amd64obsd_r_reg_offset);
222  tdep->sizeof_gregset = 24 * 8;
223
224  set_gdbarch_regset_from_core_section (gdbarch,
225					amd64obsd_regset_from_core_section);
226
227  tdep->jb_pc_offset = 7 * 8;
228
229  tdep->sigtramp_p = amd64obsd_sigtramp_p;
230  tdep->sigcontext_addr = amd64obsd_sigcontext_addr;
231  tdep->sc_reg_offset = amd64obsd_sc_reg_offset;
232  tdep->sc_num_regs = ARRAY_SIZE (amd64obsd_sc_reg_offset);
233
234  /* OpenBSD uses SVR4-style shared libraries.  */
235  set_solib_svr4_fetch_link_map_offsets
236    (gdbarch, svr4_lp64_fetch_link_map_offsets);
237}
238
239
240/* Provide a prototype to silence -Wmissing-prototypes.  */
241void _initialize_amd64obsd_tdep (void);
242
243void
244_initialize_amd64obsd_tdep (void)
245{
246  /* The OpenBSD/amd64 native dependent code makes this assumption.  */
247  gdb_assert (ARRAY_SIZE (amd64obsd_r_reg_offset) == AMD64_NUM_GREGS);
248
249  gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64,
250			  GDB_OSABI_OPENBSD_ELF, amd64obsd_init_abi);
251
252  /* OpenBSD uses traditional (a.out) NetBSD-style core dumps.  */
253  gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64,
254			  GDB_OSABI_NETBSD_AOUT, amd64obsd_init_abi);
255}
256