2012-05-11 Yao Qi <yao@codesourcery.com>
[external/binutils.git] / gdb / alpha-linux-tdep.c
1 /* Target-dependent code for GNU/Linux on Alpha.
2    Copyright (C) 2002-2003, 2007-2012 Free Software Foundation, Inc.
3
4    This file is part of GDB.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 #include "defs.h"
20 #include "frame.h"
21 #include "gdb_assert.h"
22 #include "gdb_string.h"
23 #include "osabi.h"
24 #include "solib-svr4.h"
25 #include "symtab.h"
26 #include "regset.h"
27 #include "regcache.h"
28 #include "linux-tdep.h"
29 #include "alpha-tdep.h"
30
31 /* Under GNU/Linux, signal handler invocations can be identified by
32    the designated code sequence that is used to return from a signal
33    handler.  In particular, the return address of a signal handler
34    points to a sequence that copies $sp to $16, loads $0 with the
35    appropriate syscall number, and finally enters the kernel.
36
37    This is somewhat complicated in that:
38      (1) the expansion of the "mov" assembler macro has changed over
39          time, from "bis src,src,dst" to "bis zero,src,dst",
40      (2) the kernel has changed from using "addq" to "lda" to load the
41          syscall number,
42      (3) there is a "normal" sigreturn and an "rt" sigreturn which
43          has a different stack layout.  */
44
45 static long
46 alpha_linux_sigtramp_offset_1 (struct gdbarch *gdbarch, CORE_ADDR pc)
47 {
48   switch (alpha_read_insn (gdbarch, pc))
49     {
50     case 0x47de0410:            /* bis $30,$30,$16 */
51     case 0x47fe0410:            /* bis $31,$30,$16 */
52       return 0;
53
54     case 0x43ecf400:            /* addq $31,103,$0 */
55     case 0x201f0067:            /* lda $0,103($31) */
56     case 0x201f015f:            /* lda $0,351($31) */
57       return 4;
58
59     case 0x00000083:            /* call_pal callsys */
60       return 8;
61
62     default:
63       return -1;
64     }
65 }
66
67 static LONGEST
68 alpha_linux_sigtramp_offset (struct gdbarch *gdbarch, CORE_ADDR pc)
69 {
70   long i, off;
71
72   if (pc & 3)
73     return -1;
74
75   /* Guess where we might be in the sequence.  */
76   off = alpha_linux_sigtramp_offset_1 (gdbarch, pc);
77   if (off < 0)
78     return -1;
79
80   /* Verify that the other two insns of the sequence are as we expect.  */
81   pc -= off;
82   for (i = 0; i < 12; i += 4)
83     {
84       if (i == off)
85         continue;
86       if (alpha_linux_sigtramp_offset_1 (gdbarch, pc + i) != i)
87         return -1;
88     }
89
90   return off;
91 }
92
93 static int
94 alpha_linux_pc_in_sigtramp (struct gdbarch *gdbarch,
95                             CORE_ADDR pc, const char *func_name)
96 {
97   return alpha_linux_sigtramp_offset (gdbarch, pc) >= 0;
98 }
99
100 static CORE_ADDR
101 alpha_linux_sigcontext_addr (struct frame_info *this_frame)
102 {
103   struct gdbarch *gdbarch = get_frame_arch (this_frame);
104   CORE_ADDR pc;
105   ULONGEST sp;
106   long off;
107
108   pc = get_frame_pc (this_frame);
109   sp = get_frame_register_unsigned (this_frame, ALPHA_SP_REGNUM);
110
111   off = alpha_linux_sigtramp_offset (gdbarch, pc);
112   gdb_assert (off >= 0);
113
114   /* __NR_rt_sigreturn has a couple of structures on the stack.  This is:
115
116         struct rt_sigframe {
117           struct siginfo info;
118           struct ucontext uc;
119         };
120
121         offsetof (struct rt_sigframe, uc.uc_mcontext);  */
122
123   if (alpha_read_insn (gdbarch, pc - off + 4) == 0x201f015f)
124     return sp + 176;
125
126   /* __NR_sigreturn has the sigcontext structure at the top of the stack.  */
127   return sp;
128 }
129
130 /* Supply register REGNUM from the buffer specified by GREGS and LEN
131    in the general-purpose register set REGSET to register cache
132    REGCACHE.  If REGNUM is -1, do this for all registers in REGSET.  */
133
134 static void
135 alpha_linux_supply_gregset (const struct regset *regset,
136                             struct regcache *regcache,
137                             int regnum, const void *gregs, size_t len)
138 {
139   const gdb_byte *regs = gregs;
140   int i;
141   gdb_assert (len >= 32 * 8);
142
143   for (i = 0; i < ALPHA_ZERO_REGNUM; i++)
144     {
145       if (regnum == i || regnum == -1)
146         regcache_raw_supply (regcache, i, regs + i * 8);
147     }
148
149   if (regnum == ALPHA_PC_REGNUM || regnum == -1)
150     regcache_raw_supply (regcache, ALPHA_PC_REGNUM, regs + 31 * 8);
151
152   if (regnum == ALPHA_UNIQUE_REGNUM || regnum == -1)
153     regcache_raw_supply (regcache, ALPHA_UNIQUE_REGNUM,
154                          len >= 33 * 8 ? regs + 32 * 8 : NULL);
155 }
156
157 /* Supply register REGNUM from the buffer specified by FPREGS and LEN
158    in the floating-point register set REGSET to register cache
159    REGCACHE.  If REGNUM is -1, do this for all registers in REGSET.  */
160
161 static void
162 alpha_linux_supply_fpregset (const struct regset *regset,
163                              struct regcache *regcache,
164                              int regnum, const void *fpregs, size_t len)
165 {
166   const gdb_byte *regs = fpregs;
167   int i;
168   gdb_assert (len >= 32 * 8);
169
170   for (i = ALPHA_FP0_REGNUM; i < ALPHA_FP0_REGNUM + 31; i++)
171     {
172       if (regnum == i || regnum == -1)
173         regcache_raw_supply (regcache, i, regs + (i - ALPHA_FP0_REGNUM) * 8);
174     }
175
176   if (regnum == ALPHA_FPCR_REGNUM || regnum == -1)
177     regcache_raw_supply (regcache, ALPHA_FPCR_REGNUM, regs + 31 * 8);
178 }
179
180 static struct regset alpha_linux_gregset =
181 {
182   NULL,
183   alpha_linux_supply_gregset
184 };
185
186 static struct regset alpha_linux_fpregset =
187 {
188   NULL,
189   alpha_linux_supply_fpregset
190 };
191
192 /* Return the appropriate register set for the core section identified
193    by SECT_NAME and SECT_SIZE.  */
194
195 static const struct regset *
196 alpha_linux_regset_from_core_section (struct gdbarch *gdbarch,
197                                       const char *sect_name, size_t sect_size)
198 {
199   if (strcmp (sect_name, ".reg") == 0 && sect_size >= 32 * 8)
200     return &alpha_linux_gregset;
201
202   if (strcmp (sect_name, ".reg2") == 0 && sect_size >= 32 * 8)
203     return &alpha_linux_fpregset;
204
205   return NULL;
206 }
207
208 static void
209 alpha_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
210 {
211   struct gdbarch_tdep *tdep;
212
213   linux_init_abi (info, gdbarch);
214
215   /* Hook into the DWARF CFI frame unwinder.  */
216   alpha_dwarf2_init_abi (info, gdbarch);
217
218   /* Hook into the MDEBUG frame unwinder.  */
219   alpha_mdebug_init_abi (info, gdbarch);
220
221   tdep = gdbarch_tdep (gdbarch);
222   tdep->dynamic_sigtramp_offset = alpha_linux_sigtramp_offset;
223   tdep->sigcontext_addr = alpha_linux_sigcontext_addr;
224   tdep->pc_in_sigtramp = alpha_linux_pc_in_sigtramp;
225   tdep->jb_pc = 2;
226   tdep->jb_elt_size = 8;
227
228   set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
229
230   set_solib_svr4_fetch_link_map_offsets
231     (gdbarch, svr4_lp64_fetch_link_map_offsets);
232
233   /* Enable TLS support.  */
234   set_gdbarch_fetch_tls_load_module_address (gdbarch,
235                                              svr4_fetch_objfile_link_map);
236
237   set_gdbarch_regset_from_core_section
238     (gdbarch, alpha_linux_regset_from_core_section);
239 }
240
241 /* Provide a prototype to silence -Wmissing-prototypes.  */
242 extern initialize_file_ftype _initialize_alpha_linux_tdep;
243
244 void
245 _initialize_alpha_linux_tdep (void)
246 {
247   gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_LINUX,
248                           alpha_linux_init_abi);
249 }