1/* Target-dependent code for GNU/Linux on Alpha.
2   Copyright (C) 2002, 2003, 2007 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
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
46static long
47alpha_linux_sigtramp_offset_1 (CORE_ADDR pc)
48{
49  switch (alpha_read_insn (pc))
50    {
51    case 0x47de0410:		/* bis $30,$30,$16 */
52    case 0x47fe0410:		/* bis $31,$30,$16 */
53      return 0;
54
55    case 0x43ecf400:		/* addq $31,103,$0 */
56    case 0x201f0067:		/* lda $0,103($31) */
57    case 0x201f015f:		/* lda $0,351($31) */
58      return 4;
59
60    case 0x00000083:		/* call_pal callsys */
61      return 8;
62
63    default:
64      return -1;
65    }
66}
67
68static LONGEST
69alpha_linux_sigtramp_offset (CORE_ADDR pc)
70{
71  long i, off;
72
73  if (pc & 3)
74    return -1;
75
76  /* Guess where we might be in the sequence.  */
77  off = alpha_linux_sigtramp_offset_1 (pc);
78  if (off < 0)
79    return -1;
80
81  /* Verify that the other two insns of the sequence are as we expect.  */
82  pc -= off;
83  for (i = 0; i < 12; i += 4)
84    {
85      if (i == off)
86	continue;
87      if (alpha_linux_sigtramp_offset_1 (pc + i) != i)
88	return -1;
89    }
90
91  return off;
92}
93
94static int
95alpha_linux_pc_in_sigtramp (CORE_ADDR pc, char *func_name)
96{
97  return alpha_linux_sigtramp_offset (pc) >= 0;
98}
99
100static CORE_ADDR
101alpha_linux_sigcontext_addr (struct frame_info *next_frame)
102{
103  CORE_ADDR pc;
104  ULONGEST sp;
105  long off;
106
107  pc = frame_pc_unwind (next_frame);
108  frame_unwind_unsigned_register (next_frame, ALPHA_SP_REGNUM, &sp);
109
110  off = alpha_linux_sigtramp_offset (pc);
111  gdb_assert (off >= 0);
112
113  /* __NR_rt_sigreturn has a couple of structures on the stack.  This is:
114
115	struct rt_sigframe {
116	  struct siginfo info;
117	  struct ucontext uc;
118        };
119
120	offsetof (struct rt_sigframe, uc.uc_mcontext);
121  */
122  if (alpha_read_insn (pc - off + 4) == 0x201f015f)
123    return sp + 176;
124
125  /* __NR_sigreturn has the sigcontext structure at the top of the stack.  */
126  return sp;
127}
128
129/* Supply register REGNUM from the buffer specified by GREGS and LEN
130   in the general-purpose register set REGSET to register cache
131   REGCACHE.  If REGNUM is -1, do this for all registers in REGSET.  */
132
133static void
134alpha_linux_supply_gregset (const struct regset *regset,
135			    struct regcache *regcache,
136			    int regnum, const void *gregs, size_t len)
137{
138  const gdb_byte *regs = gregs;
139  int i;
140  gdb_assert (len >= 32 * 8);
141
142  for (i = 0; i < ALPHA_ZERO_REGNUM; i++)
143    {
144      if (regnum == i || regnum == -1)
145	regcache_raw_supply (regcache, i, regs + i * 8);
146    }
147
148  if (regnum == ALPHA_PC_REGNUM || regnum == -1)
149    regcache_raw_supply (regcache, ALPHA_PC_REGNUM, regs + 31 * 8);
150
151  if (regnum == ALPHA_UNIQUE_REGNUM || regnum == -1)
152    regcache_raw_supply (regcache, ALPHA_UNIQUE_REGNUM,
153			 len >= 33 * 8 ? regs + 32 * 8 : NULL);
154}
155
156/* Supply register REGNUM from the buffer specified by FPREGS and LEN
157   in the floating-point register set REGSET to register cache
158   REGCACHE.  If REGNUM is -1, do this for all registers in REGSET.  */
159
160static void
161alpha_linux_supply_fpregset (const struct regset *regset,
162			     struct regcache *regcache,
163			     int regnum, const void *fpregs, size_t len)
164{
165  const gdb_byte *regs = fpregs;
166  int i;
167  gdb_assert (len >= 32 * 8);
168
169  for (i = ALPHA_FP0_REGNUM; i < ALPHA_FP0_REGNUM + 31; i++)
170    {
171      if (regnum == i || regnum == -1)
172	regcache_raw_supply (regcache, i, regs + (i - ALPHA_FP0_REGNUM) * 8);
173    }
174
175  if (regnum == ALPHA_FPCR_REGNUM || regnum == -1)
176    regcache_raw_supply (regcache, ALPHA_FPCR_REGNUM, regs + 31 * 8);
177}
178
179static struct regset alpha_linux_gregset =
180{
181  NULL,
182  alpha_linux_supply_gregset
183};
184
185static struct regset alpha_linux_fpregset =
186{
187  NULL,
188  alpha_linux_supply_fpregset
189};
190
191/* Return the appropriate register set for the core section identified
192   by SECT_NAME and SECT_SIZE.  */
193
194const struct regset *
195alpha_linux_regset_from_core_section (struct gdbarch *gdbarch,
196				      const char *sect_name, size_t sect_size)
197{
198  if (strcmp (sect_name, ".reg") == 0 && sect_size >= 32 * 8)
199    return &alpha_linux_gregset;
200
201  if (strcmp (sect_name, ".reg2") == 0 && sect_size >= 32 * 8)
202    return &alpha_linux_fpregset;
203
204  return NULL;
205}
206
207static void
208alpha_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
209{
210  struct gdbarch_tdep *tdep;
211
212  /* Hook into the DWARF CFI frame unwinder.  */
213  alpha_dwarf2_init_abi (info, gdbarch);
214
215  /* Hook into the MDEBUG frame unwinder.  */
216  alpha_mdebug_init_abi (info, gdbarch);
217
218  tdep = gdbarch_tdep (gdbarch);
219  tdep->dynamic_sigtramp_offset = alpha_linux_sigtramp_offset;
220  tdep->sigcontext_addr = alpha_linux_sigcontext_addr;
221  tdep->pc_in_sigtramp = alpha_linux_pc_in_sigtramp;
222  tdep->jb_pc = 2;
223  tdep->jb_elt_size = 8;
224
225  set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
226
227  /* Enable TLS support.  */
228  set_gdbarch_fetch_tls_load_module_address (gdbarch,
229                                             svr4_fetch_objfile_link_map);
230
231  set_gdbarch_regset_from_core_section
232    (gdbarch, alpha_linux_regset_from_core_section);
233}
234
235void
236_initialize_alpha_linux_tdep (void)
237{
238  gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_LINUX,
239                          alpha_linux_init_abi);
240}
241