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