1/* Target-dependent code for GNU/Linux x86-64.
2
3   Copyright 2001, 2003, 2004 Free Software Foundation, Inc.
4   Contributed by Jiri Smid, SuSE Labs.
5
6   This file is part of GDB.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place - Suite 330,
21   Boston, MA 02111-1307, USA.  */
22
23#include "defs.h"
24#include "frame.h"
25#include "gdbcore.h"
26#include "regcache.h"
27#include "osabi.h"
28#include "symtab.h"
29
30#include "gdb_string.h"
31
32#include "amd64-tdep.h"
33#include "solib-svr4.h"
34
35/* Mapping between the general-purpose registers in `struct user'
36   format and GDB's register cache layout.  */
37
38/* From <sys/reg.h>.  */
39static int amd64_linux_gregset_reg_offset[] =
40{
41  10 * 8,			/* %rax */
42  5 * 8,			/* %rbx */
43  11 * 8,			/* %rcx */
44  12 * 8,			/* %rdx */
45  13 * 8,			/* %rsi */
46  14 * 8,			/* %rdi */
47  4 * 8,			/* %rbp */
48  19 * 8,			/* %rsp */
49  9 * 8,			/* %r8 ... */
50  8 * 8,
51  7 * 8,
52  6 * 8,
53  3 * 8,
54  2 * 8,
55  1 * 8,
56  0 * 8,			/* ... %r15 */
57  16 * 8,			/* %rip */
58  18 * 8,			/* %eflags */
59  17 * 8,			/* %cs */
60  20 * 8,			/* %ss */
61  23 * 8,			/* %ds */
62  24 * 8,			/* %es */
63  25 * 8,			/* %fs */
64  26 * 8			/* %gs */
65};
66
67
68/* Support for signal handlers.  */
69
70#define LINUX_SIGTRAMP_INSN0	0x48	/* mov $NNNNNNNN, %rax */
71#define LINUX_SIGTRAMP_OFFSET0	0
72#define LINUX_SIGTRAMP_INSN1	0x0f	/* syscall */
73#define LINUX_SIGTRAMP_OFFSET1	7
74
75static const unsigned char linux_sigtramp_code[] =
76{
77  /* mov $__NR_rt_sigreturn, %rax */
78  LINUX_SIGTRAMP_INSN0, 0xc7, 0xc0, 0x0f, 0x00, 0x00, 0x00,
79  /* syscall */
80  LINUX_SIGTRAMP_INSN1, 0x05
81};
82
83#define LINUX_SIGTRAMP_LEN (sizeof linux_sigtramp_code)
84
85/* If PC is in a sigtramp routine, return the address of the start of
86   the routine.  Otherwise, return 0.  */
87
88static CORE_ADDR
89amd64_linux_sigtramp_start (struct frame_info *next_frame)
90{
91  CORE_ADDR pc = frame_pc_unwind (next_frame);
92  unsigned char buf[LINUX_SIGTRAMP_LEN];
93
94  /* We only recognize a signal trampoline if PC is at the start of
95     one of the two instructions.  We optimize for finding the PC at
96     the start, as will be the case when the trampoline is not the
97     first frame on the stack.  We assume that in the case where the
98     PC is not at the start of the instruction sequence, there will be
99     a few trailing readable bytes on the stack.  */
100
101  if (!safe_frame_unwind_memory (next_frame, pc, buf, LINUX_SIGTRAMP_LEN))
102    return 0;
103
104  if (buf[0] != LINUX_SIGTRAMP_INSN0)
105    {
106      if (buf[0] != LINUX_SIGTRAMP_INSN1)
107	return 0;
108
109      pc -= LINUX_SIGTRAMP_OFFSET1;
110
111      if (!safe_frame_unwind_memory (next_frame, pc, buf, LINUX_SIGTRAMP_LEN))
112	return 0;
113    }
114
115  if (memcmp (buf, linux_sigtramp_code, LINUX_SIGTRAMP_LEN) != 0)
116    return 0;
117
118  return pc;
119}
120
121/* Return whether the frame preceding NEXT_FRAME corresponds to a
122   GNU/Linux sigtramp routine.  */
123
124static int
125amd64_linux_sigtramp_p (struct frame_info *next_frame)
126{
127  CORE_ADDR pc = frame_pc_unwind (next_frame);
128  char *name;
129
130  find_pc_partial_function (pc, &name, NULL, NULL);
131
132  /* If we have NAME, we can optimize the search.  The trampoline is
133     named __restore_rt.  However, it isn't dynamically exported from
134     the shared C library, so the trampoline may appear to be part of
135     the preceding function.  This should always be sigaction,
136     __sigaction, or __libc_sigaction (all aliases to the same
137     function).  */
138  if (name == NULL || strstr (name, "sigaction") != NULL)
139    return (amd64_linux_sigtramp_start (next_frame) != 0);
140
141  return (strcmp ("__restore_rt", name) == 0);
142}
143
144/* Offset to struct sigcontext in ucontext, from <asm/ucontext.h>.  */
145#define AMD64_LINUX_UCONTEXT_SIGCONTEXT_OFFSET 40
146
147/* Assuming NEXT_FRAME is a frame following a GNU/Linux sigtramp
148   routine, return the address of the associated sigcontext structure.  */
149
150static CORE_ADDR
151amd64_linux_sigcontext_addr (struct frame_info *next_frame)
152{
153  CORE_ADDR sp;
154  char buf[8];
155
156  frame_unwind_register (next_frame, SP_REGNUM, buf);
157  sp = extract_unsigned_integer (buf, 8);
158
159  /* The sigcontext structure is part of the user context.  A pointer
160     to the user context is passed as the third argument to the signal
161     handler, i.e. in %rdx.  Unfortunately %rdx isn't preserved across
162     function calls so we can't use it.  Fortunately the user context
163     is part of the signal frame and the unwound %rsp directly points
164     at it.  */
165  return sp + AMD64_LINUX_UCONTEXT_SIGCONTEXT_OFFSET;
166}
167
168
169/* From <asm/sigcontext.h>.  */
170static int amd64_linux_sc_reg_offset[] =
171{
172  13 * 8,			/* %rax */
173  11 * 8,			/* %rbx */
174  14 * 8,			/* %rcx */
175  12 * 8,			/* %rdx */
176  9 * 8,			/* %rsi */
177  8 * 8,			/* %rdi */
178  10 * 8,			/* %rbp */
179  15 * 8,			/* %rsp */
180  0 * 8,			/* %r8 */
181  1 * 8,			/* %r9 */
182  2 * 8,			/* %r10 */
183  3 * 8,			/* %r11 */
184  4 * 8,			/* %r12 */
185  5 * 8,			/* %r13 */
186  6 * 8,			/* %r14 */
187  7 * 8,			/* %r15 */
188  16 * 8,			/* %rip */
189  17 * 8,			/* %eflags */
190
191  /* FIXME: kettenis/2002030531: The registers %cs, %fs and %gs are
192     available in `struct sigcontext'.  However, they only occupy two
193     bytes instead of four, which makes using them here rather
194     difficult.  Leave them out for now.  */
195  -1,				/* %cs */
196  -1,				/* %ss */
197  -1,				/* %ds */
198  -1,				/* %es */
199  -1,				/* %fs */
200  -1				/* %gs */
201};
202
203static void
204amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
205{
206  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
207
208  tdep->gregset_reg_offset = amd64_linux_gregset_reg_offset;
209  tdep->gregset_num_regs = ARRAY_SIZE (amd64_linux_gregset_reg_offset);
210  tdep->sizeof_gregset = 27 * 8;
211
212  amd64_init_abi (info, gdbarch);
213
214  tdep->sigtramp_p = amd64_linux_sigtramp_p;
215  tdep->sigcontext_addr = amd64_linux_sigcontext_addr;
216  tdep->sc_reg_offset = amd64_linux_sc_reg_offset;
217  tdep->sc_num_regs = ARRAY_SIZE (amd64_linux_sc_reg_offset);
218
219  /* GNU/Linux uses SVR4-style shared libraries.  */
220  set_solib_svr4_fetch_link_map_offsets
221    (gdbarch, svr4_lp64_fetch_link_map_offsets);
222}
223
224
225/* Provide a prototype to silence -Wmissing-prototypes.  */
226extern void _initialize_amd64_linux_tdep (void);
227
228void
229_initialize_amd64_linux_tdep (void)
230{
231  gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64,
232			  GDB_OSABI_LINUX, amd64_linux_init_abi);
233}
234