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 "osabi.h"
26#include "regset.h"
27#include "target.h"
28
29#include "gdb_assert.h"
30#include "gdb_string.h"
31
32#include "amd64-tdep.h"
33#include "i387-tdep.h"
34#include "solib-svr4.h"
35
36/* Support for core dumps.  */
37
38static void
39amd64obsd_supply_regset (const struct regset *regset,
40			 struct regcache *regcache, int regnum,
41			 const void *regs, size_t len)
42{
43  const struct gdbarch_tdep *tdep = regset->descr;
44
45  gdb_assert (len >= tdep->sizeof_gregset + I387_SIZEOF_FXSAVE);
46
47  i386_supply_gregset (regset, regcache, regnum, regs, tdep->sizeof_gregset);
48  amd64_supply_fxsave (regcache, regnum, (char *)regs + tdep->sizeof_gregset);
49}
50
51static const struct regset *
52amd64obsd_regset_from_core_section (struct gdbarch *gdbarch,
53				    const char *sect_name, size_t sect_size)
54{
55  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
56
57  /* OpenBSD core dumps don't use seperate register sets for the
58     general-purpose and floating-point registers.  */
59
60  if (strcmp (sect_name, ".reg") == 0
61      && sect_size >= tdep->sizeof_gregset + I387_SIZEOF_FXSAVE)
62    {
63      if (tdep->gregset == NULL)
64	{
65	  tdep->gregset = XMALLOC (struct regset);
66	  tdep->gregset->descr = tdep;
67	  tdep->gregset->supply_regset = amd64obsd_supply_regset;
68	}
69      return tdep->gregset;
70    }
71
72  return NULL;
73}
74
75
76/* Support for signal handlers.  */
77
78static const int amd64obsd_page_size = 4096;
79
80static int
81amd64obsd_pc_in_sigtramp (CORE_ADDR pc, char *name)
82{
83  CORE_ADDR start_pc = (pc & ~(amd64obsd_page_size - 1));
84  const char sigreturn[] =
85  {
86    0x48, 0xc7, 0xc0,
87    0x67, 0x00, 0x00, 0x00,	/* movq $SYS_sigreturn, %rax */
88    0xcd, 0x80			/* int $0x80 */
89  };
90  char *buf;
91
92  if (name)
93    return 0;
94
95  /* If we can't read the instructions at START_PC, return zero.  */
96  buf = alloca (sizeof sigreturn);
97  if (target_read_memory (start_pc + 0x7, buf, sizeof sigreturn))
98    return 0;
99
100  /* Check for sigreturn(2).  */
101  if (memcmp (buf, sigreturn, sizeof sigreturn))
102    return 0;
103
104  return 1;
105}
106
107/* Assuming NEXT_FRAME is for a frame following a BSD sigtramp
108   routine, return the address of the associated sigcontext structure.  */
109
110static CORE_ADDR
111amd64obsd_sigcontext_addr (struct frame_info *next_frame)
112{
113  /* The %rsp register points at `struct sigcontext' upon entry of a
114     signal trampoline.  */
115  return frame_unwind_register_unsigned (next_frame, AMD64_RSP_REGNUM);
116}
117
118/* OpenBSD 3.5 or later.  */
119
120/* Mapping between the general-purpose registers in `struct reg'
121   format and GDB's register cache layout.  */
122
123/* From <machine/reg.h>.  */
124int amd64obsd_r_reg_offset[] =
125{
126  14 * 8,			/* %rax */
127  13 * 8,			/* %rbx */
128  3 * 8,			/* %rcx */
129  2 * 8,			/* %rdx */
130  1 * 8,			/* %rsi */
131  0 * 8,			/* %rdi */
132  12 * 8,			/* %rbp */
133  15 * 8,			/* %rsp */
134  4 * 8,			/* %r8 .. */
135  5 * 8,
136  6 * 8,
137  7 * 8,
138  8 * 8,
139  9 * 8,
140  10 * 8,
141  11 * 8,			/* ... %r15 */
142  16 * 8,			/* %rip */
143  17 * 8,			/* %eflags */
144  18 * 8,			/* %cs */
145  19 * 8,			/* %ss */
146  20 * 8,			/* %ds */
147  21 * 8,			/* %es */
148  22 * 8,			/* %fs */
149  23 * 8			/* %gs */
150};
151
152/* From <machine/signal.h>.  */
153static int amd64obsd_sc_reg_offset[] =
154{
155  14 * 8,			/* %rax */
156  13 * 8,			/* %rbx */
157  3 * 8,			/* %rcx */
158  2 * 8,			/* %rdx */
159  1 * 8,			/* %rsi */
160  0 * 8,			/* %rdi */
161  12 * 8,			/* %rbp */
162  24 * 8,			/* %rsp */
163  4 * 8,			/* %r8 ... */
164  5 * 8,
165  6 * 8,
166  7 * 8,
167  8 * 8,
168  9 * 8,
169  10 * 8,
170  11 * 8,			/* ... %r15 */
171  21 * 8,			/* %rip */
172  23 * 8,			/* %eflags */
173  22 * 8,			/* %cs */
174  25 * 8,			/* %ss */
175  18 * 8,			/* %ds */
176  17 * 8,			/* %es */
177  16 * 8,			/* %fs */
178  15 * 8			/* %gs */
179};
180
181static void
182amd64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
183{
184  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
185
186  amd64_init_abi (info, gdbarch);
187
188  /* Initialize general-purpose register set details.  */
189  tdep->gregset_reg_offset = amd64obsd_r_reg_offset;
190  tdep->gregset_num_regs = ARRAY_SIZE (amd64obsd_r_reg_offset);
191  tdep->sizeof_gregset = 24 * 8;
192
193  set_gdbarch_regset_from_core_section (gdbarch,
194					amd64obsd_regset_from_core_section);
195
196  tdep->jb_pc_offset = 7 * 8;
197
198  set_gdbarch_pc_in_sigtramp (gdbarch, amd64obsd_pc_in_sigtramp);
199  tdep->sigcontext_addr = amd64obsd_sigcontext_addr;
200  tdep->sc_reg_offset = amd64obsd_sc_reg_offset;
201  tdep->sc_num_regs = ARRAY_SIZE (amd64obsd_sc_reg_offset);
202
203  /* OpenBSD uses SVR4-style shared libraries.  */
204  set_solib_svr4_fetch_link_map_offsets
205    (gdbarch, svr4_lp64_fetch_link_map_offsets);
206}
207
208
209/* Provide a prototype to silence -Wmissing-prototypes.  */
210void _initialize_amd64obsd_tdep (void);
211
212void
213_initialize_amd64obsd_tdep (void)
214{
215  /* The OpenBSD/amd64 native dependent code makes this assumption.  */
216  gdb_assert (ARRAY_SIZE (amd64obsd_r_reg_offset) == AMD64_NUM_GREGS);
217
218  gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64,
219			  GDB_OSABI_OPENBSD_ELF, amd64obsd_init_abi);
220
221  /* OpenBSD uses traditional (a.out) NetBSD-style core dumps.  */
222  gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64,
223			  GDB_OSABI_NETBSD_AOUT, amd64obsd_init_abi);
224}
225