Reindented.
[external/binutils.git] / gdb / x86-64-linux-tdep.c
1 /* Target-dependent code for GNU/Linux running on x86-64, for GDB.
2
3    Copyright 2001 Free Software Foundation, Inc.
4
5    Contributed by Jiri Smid, SuSE Labs.
6
7    This file is part of GDB.
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 59 Temple Place - Suite 330,
22    Boston, MA 02111-1307, USA.  */
23
24 #include "defs.h"
25 #include "inferior.h"
26 #include "gdbcore.h"
27 #include "gdb_string.h"
28 #include "regcache.h"
29 #include "x86-64-tdep.h"
30 #include "dwarf2cfi.h"
31
32 #define LINUX_SIGTRAMP_INSN0 (0x48)     /* mov $NNNNNNNN,%rax */
33 #define LINUX_SIGTRAMP_OFFSET0 (0)
34 #define LINUX_SIGTRAMP_INSN1 (0x0f)     /* syscall */
35 #define LINUX_SIGTRAMP_OFFSET1 (7)
36
37 static const unsigned char linux_sigtramp_code[] = {
38   /*  mov $__NR_rt_sigreturn,%rax */
39   LINUX_SIGTRAMP_INSN0, 0xc7, 0xc0, 0x0f, 0x00, 0x00, 0x00,
40   /* syscall */
41   LINUX_SIGTRAMP_INSN1, 0x05
42 };
43
44 #define LINUX_SIGTRAMP_LEN (sizeof linux_sigtramp_code)
45
46 /* If PC is in a sigtramp routine, return the address of the start of
47    the routine.  Otherwise, return 0.  */
48
49 static CORE_ADDR
50 x86_64_linux_sigtramp_start (CORE_ADDR pc)
51 {
52   unsigned char buf[LINUX_SIGTRAMP_LEN];
53   if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0)
54     return 0;
55
56   if (buf[0] != LINUX_SIGTRAMP_INSN0)
57     {
58       if (buf[0] != LINUX_SIGTRAMP_INSN1)
59         return 0;
60
61       pc -= LINUX_SIGTRAMP_OFFSET1;
62
63       if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0)
64         return 0;
65     }
66
67   if (memcmp (buf, linux_sigtramp_code, LINUX_SIGTRAMP_LEN) != 0)
68     return 0;
69
70   return pc;
71 }
72
73 #define LINUX_SIGINFO_SIZE 0
74
75 /* Offset to struct sigcontext in ucontext, from <asm/ucontext.h>.  */
76 #define LINUX_UCONTEXT_SIGCONTEXT_OFFSET 40
77
78 /* Offset to saved PC in sigcontext, from <asm/sigcontext.h>.  */
79 #define LINUX_SIGCONTEXT_PC_OFFSET 128
80 #define LINUX_SIGCONTEXT_FP_OFFSET 120
81
82 /* Assuming FRAME is for a GNU/Linux sigtramp routine, return the
83    address of the associated sigcontext structure.  */
84 static CORE_ADDR
85 x86_64_linux_sigcontext_addr (struct frame_info *frame)
86 {
87   CORE_ADDR pc;
88   ULONGEST rsp;
89
90   pc = x86_64_linux_sigtramp_start (frame->pc);
91   if (pc)
92     {
93       if (frame->next)
94         /* If this isn't the top frame, the next frame must be for the
95            signal handler itself.  The sigcontext structure is part of
96            the user context. */
97         return frame->next->frame + LINUX_SIGINFO_SIZE +
98           LINUX_UCONTEXT_SIGCONTEXT_OFFSET;
99
100
101       /* This is the top frame. */
102       rsp = read_register (SP_REGNUM);
103       return rsp + LINUX_SIGINFO_SIZE + LINUX_UCONTEXT_SIGCONTEXT_OFFSET;
104
105     }
106
107   error ("Couldn't recognize signal trampoline.");
108   return 0;
109 }
110
111 /* Assuming FRAME is for a GNU/Linux sigtramp routine, return the
112    saved program counter.  */
113
114 static CORE_ADDR
115 x86_64_linux_sigtramp_saved_pc (struct frame_info *frame)
116 {
117   CORE_ADDR addr;
118
119   addr = x86_64_linux_sigcontext_addr (frame);
120   return read_memory_integer (addr + LINUX_SIGCONTEXT_PC_OFFSET, 8);
121 }
122
123 /* Immediately after a function call, return the saved pc.  */
124
125 CORE_ADDR
126 x86_64_linux_saved_pc_after_call (struct frame_info *frame)
127 {
128   if (frame->signal_handler_caller)
129     return x86_64_linux_sigtramp_saved_pc (frame);
130
131   return read_memory_integer (read_register (SP_REGNUM), 8);
132 }
133
134 /* Saved Pc.  Get it from sigcontext if within sigtramp.  */
135 CORE_ADDR
136 x86_64_linux_frame_saved_pc (struct frame_info *frame)
137 {
138   if (frame->signal_handler_caller)
139     return x86_64_linux_sigtramp_saved_pc (frame);
140   return cfi_get_ra (frame);
141 }
142
143 /* Return whether PC is in a GNU/Linux sigtramp routine.  */
144
145 int
146 x86_64_linux_in_sigtramp (CORE_ADDR pc, char *name)
147 {
148   if (name)
149     return STREQ ("__restore_rt", name);
150
151   return (x86_64_linux_sigtramp_start (pc) != 0);
152 }
153
154 CORE_ADDR
155 x86_64_linux_frame_chain (struct frame_info *fi)
156 {
157   ULONGEST addr;
158   CORE_ADDR fp, pc;
159
160   if (!fi->signal_handler_caller)
161     {
162       fp = cfi_frame_chain (fi);
163       if (fp)
164         return fp;
165       else
166         addr = fi->frame;
167     }
168   else
169     addr = fi->next->frame;
170
171   addr += LINUX_SIGINFO_SIZE + LINUX_UCONTEXT_SIGCONTEXT_OFFSET;
172
173   fp = read_memory_integer (addr + LINUX_SIGCONTEXT_FP_OFFSET, 8) + 8;
174
175   return fp;
176 }
177
178 void
179 x86_64_init_frame_pc (int fromleaf, struct frame_info *fi)
180 {
181   CORE_ADDR addr;
182
183   if (fi->next && fi->next->signal_handler_caller)
184     {
185       addr = fi->next->next->frame
186         + LINUX_SIGINFO_SIZE + LINUX_UCONTEXT_SIGCONTEXT_OFFSET;
187       fi->pc = read_memory_integer (addr + LINUX_SIGCONTEXT_PC_OFFSET, 8);
188     }
189   else
190     cfi_init_frame_pc (fromleaf, fi);
191 }
192
193 void
194 x86_64_init_extra_frame_info (int fromleaf, struct frame_info *fi)
195 {
196   cfi_init_extra_frame_info (fromleaf, fi);
197 }