1/* Motorola m68k target-dependent support for GNU/Linux.
2
3   Copyright 1996, 1998, 2000, 2001, 2002, 2003, 2004
4   Free Software Foundation, Inc.
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 "gdbcore.h"
25#include "doublest.h"
26#include "floatformat.h"
27#include "frame.h"
28#include "target.h"
29#include "gdb_string.h"
30#include "gdbtypes.h"
31#include "osabi.h"
32#include "regcache.h"
33#include "objfiles.h"
34#include "symtab.h"
35#include "m68k-tdep.h"
36#include "trad-frame.h"
37#include "frame-unwind.h"
38
39/* Offsets (in target ints) into jmp_buf.  */
40
41#define M68K_LINUX_JB_ELEMENT_SIZE 4
42#define M68K_LINUX_JB_PC 7
43
44/* Check whether insn1 and insn2 are parts of a signal trampoline.  */
45
46#define IS_SIGTRAMP(insn1, insn2)					\
47  (/* addaw #20,sp; moveq #119,d0; trap #0 */				\
48   (insn1 == 0xdefc0014 && insn2 == 0x70774e40)				\
49   /* moveq #119,d0; trap #0 */						\
50   || insn1 == 0x70774e40)
51
52#define IS_RT_SIGTRAMP(insn1, insn2)					\
53  (/* movel #173,d0; trap #0 */						\
54   (insn1 == 0x203c0000 && insn2 == 0x00ad4e40)				\
55   /* moveq #82,d0; notb d0; trap #0 */					\
56   || (insn1 == 0x70524600 && (insn2 >> 16) == 0x4e40))
57
58/* Return non-zero if PC points into the signal trampoline.  For the
59   sake of m68k_linux_get_sigtramp_info we also distinguish between
60   non-RT and RT signal trampolines.  */
61
62static int
63m68k_linux_pc_in_sigtramp (CORE_ADDR pc, char *name)
64{
65  CORE_ADDR sp;
66  char buf[12];
67  unsigned long insn0, insn1, insn2;
68
69  if (deprecated_read_memory_nobpt (pc - 4, buf, sizeof (buf)))
70    return 0;
71  insn1 = extract_unsigned_integer (buf + 4, 4);
72  insn2 = extract_unsigned_integer (buf + 8, 4);
73  if (IS_SIGTRAMP (insn1, insn2))
74    return 1;
75  if (IS_RT_SIGTRAMP (insn1, insn2))
76    return 2;
77
78  insn0 = extract_unsigned_integer (buf, 4);
79  if (IS_SIGTRAMP (insn0, insn1))
80    return 1;
81  if (IS_RT_SIGTRAMP (insn0, insn1))
82    return 2;
83
84  insn0 = ((insn0 << 16) & 0xffffffff) | (insn1 >> 16);
85  insn1 = ((insn1 << 16) & 0xffffffff) | (insn2 >> 16);
86  if (IS_SIGTRAMP (insn0, insn1))
87    return 1;
88  if (IS_RT_SIGTRAMP (insn0, insn1))
89    return 2;
90
91  return 0;
92}
93
94/* From <asm/sigcontext.h>.  */
95static int m68k_linux_sigcontext_reg_offset[M68K_NUM_REGS] =
96{
97  2 * 4,			/* %d0 */
98  3 * 4,			/* %d1 */
99  -1,				/* %d2 */
100  -1,				/* %d3 */
101  -1,				/* %d4 */
102  -1,				/* %d5 */
103  -1,				/* %d6 */
104  -1,				/* %d7 */
105  4 * 4,			/* %a0 */
106  5 * 4,			/* %a1 */
107  -1,				/* %a2 */
108  -1,				/* %a3 */
109  -1,				/* %a4 */
110  -1,				/* %a5 */
111  -1,				/* %fp */
112  1 * 4,			/* %sp */
113  5 * 4 + 2,			/* %sr */
114  6 * 4 + 2,			/* %pc */
115  8 * 4,			/* %fp0 */
116  11 * 4,			/* %fp1 */
117  -1,				/* %fp2 */
118  -1,				/* %fp3 */
119  -1,				/* %fp4 */
120  -1,				/* %fp5 */
121  -1,				/* %fp6 */
122  -1,				/* %fp7 */
123  14 * 4,			/* %fpcr */
124  15 * 4,			/* %fpsr */
125  16 * 4			/* %fpiaddr */
126};
127
128/* From <asm/ucontext.h>.  */
129static int m68k_linux_ucontext_reg_offset[M68K_NUM_REGS] =
130{
131  6 * 4,			/* %d0 */
132  7 * 4,			/* %d1 */
133  8 * 4,			/* %d2 */
134  9 * 4,			/* %d3 */
135  10 * 4,			/* %d4 */
136  11 * 4,			/* %d5 */
137  12 * 4,			/* %d6 */
138  13 * 4,			/* %d7 */
139  14 * 4,			/* %a0 */
140  15 * 4,			/* %a1 */
141  16 * 4,			/* %a2 */
142  17 * 4,			/* %a3 */
143  18 * 4,			/* %a4 */
144  19 * 4,			/* %a5 */
145  20 * 4,			/* %fp */
146  21 * 4,			/* %sp */
147  23 * 4,			/* %sr */
148  22 * 4,			/* %pc */
149  27 * 4,			/* %fp0 */
150  30 * 4,			/* %fp1 */
151  33 * 4,			/* %fp2 */
152  36 * 4,			/* %fp3 */
153  39 * 4,			/* %fp4 */
154  42 * 4,			/* %fp5 */
155  45 * 4,			/* %fp6 */
156  48 * 4,			/* %fp7 */
157  24 * 4,			/* %fpcr */
158  25 * 4,			/* %fpsr */
159  26 * 4			/* %fpiaddr */
160};
161
162
163/* Get info about saved registers in sigtramp.  */
164
165struct m68k_linux_sigtramp_info
166{
167  /* Address of sigcontext.  */
168  CORE_ADDR sigcontext_addr;
169
170  /* Offset of registers in `struct sigcontext'.  */
171  int *sc_reg_offset;
172};
173
174static struct m68k_linux_sigtramp_info
175m68k_linux_get_sigtramp_info (struct frame_info *next_frame)
176{
177  CORE_ADDR sp;
178  char buf[4];
179  struct m68k_linux_sigtramp_info info;
180
181  frame_unwind_register (next_frame, M68K_SP_REGNUM, buf);
182  sp = extract_unsigned_integer (buf, 4);
183
184  /* Get sigcontext address, it is the third parameter on the stack.  */
185  info.sigcontext_addr = read_memory_unsigned_integer (sp + 8, 4);
186
187  if (m68k_linux_pc_in_sigtramp (frame_pc_unwind (next_frame), 0) == 2)
188    info.sc_reg_offset = m68k_linux_ucontext_reg_offset;
189  else
190    info.sc_reg_offset = m68k_linux_sigcontext_reg_offset;
191  return info;
192}
193
194/* Signal trampolines.  */
195
196static struct trad_frame_cache *
197m68k_linux_sigtramp_frame_cache (struct frame_info *next_frame,
198				 void **this_cache)
199{
200  struct frame_id this_id;
201  struct trad_frame_cache *cache;
202  struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
203  struct m68k_linux_sigtramp_info info;
204  char buf[4];
205  int i;
206
207  if (*this_cache)
208    return *this_cache;
209
210  cache = trad_frame_cache_zalloc (next_frame);
211
212  /* FIXME: cagney/2004-05-01: This is is long standing broken code.
213     The frame ID's code address should be the start-address of the
214     signal trampoline and not the current PC within that
215     trampoline.  */
216  frame_unwind_register (next_frame, M68K_SP_REGNUM, buf);
217  /* See the end of m68k_push_dummy_call.  */
218  this_id = frame_id_build (extract_unsigned_integer (buf, 4) - 4 + 8,
219			    frame_pc_unwind (next_frame));
220  trad_frame_set_id (cache, this_id);
221
222  info = m68k_linux_get_sigtramp_info (next_frame);
223
224  for (i = 0; i < M68K_NUM_REGS; i++)
225    if (info.sc_reg_offset[i] != -1)
226      trad_frame_set_reg_addr (cache, i,
227			       info.sigcontext_addr + info.sc_reg_offset[i]);
228
229  *this_cache = cache;
230  return cache;
231}
232
233static void
234m68k_linux_sigtramp_frame_this_id (struct frame_info *next_frame,
235				   void **this_cache,
236				   struct frame_id *this_id)
237{
238  struct trad_frame_cache *cache =
239    m68k_linux_sigtramp_frame_cache (next_frame, this_cache);
240  trad_frame_get_id (cache, this_id);
241}
242
243static void
244m68k_linux_sigtramp_frame_prev_register (struct frame_info *next_frame,
245					 void **this_cache,
246					 int regnum, int *optimizedp,
247					 enum lval_type *lvalp,
248					 CORE_ADDR *addrp,
249					 int *realnump, void *valuep)
250{
251  /* Make sure we've initialized the cache.  */
252  struct trad_frame_cache *cache =
253    m68k_linux_sigtramp_frame_cache (next_frame, this_cache);
254  trad_frame_get_register (cache, next_frame, regnum, optimizedp, lvalp,
255			   addrp, realnump, valuep);
256}
257
258static const struct frame_unwind m68k_linux_sigtramp_frame_unwind =
259{
260  SIGTRAMP_FRAME,
261  m68k_linux_sigtramp_frame_this_id,
262  m68k_linux_sigtramp_frame_prev_register
263};
264
265static const struct frame_unwind *
266m68k_linux_sigtramp_frame_sniffer (struct frame_info *next_frame)
267{
268  CORE_ADDR pc = frame_pc_unwind (next_frame);
269  char *name;
270
271  find_pc_partial_function (pc, &name, NULL, NULL);
272  if (m68k_linux_pc_in_sigtramp (pc, name))
273    return &m68k_linux_sigtramp_frame_unwind;
274
275  return NULL;
276}
277
278static void
279m68k_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
280{
281  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
282
283  tdep->jb_pc = M68K_LINUX_JB_PC;
284  tdep->jb_elt_size = M68K_LINUX_JB_ELEMENT_SIZE;
285
286  /* GNU/Linux uses a calling convention that's similar to SVR4.  It
287     returns integer values in %d0/%di, pointer values in %a0 and
288     floating values in %fp0, just like SVR4, but uses %a1 to pass the
289     address to store a structure value.  It also returns small
290     structures in registers instead of memory.  */
291  m68k_svr4_init_abi (info, gdbarch);
292  tdep->struct_value_regnum = M68K_A1_REGNUM;
293  tdep->struct_return = reg_struct_return;
294
295  frame_unwind_append_sniffer (gdbarch, m68k_linux_sigtramp_frame_sniffer);
296
297  /* Shared library handling.  */
298  set_gdbarch_in_solib_call_trampoline (gdbarch, in_plt_section);
299  set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
300}
301
302void
303_initialize_m68k_linux_tdep (void)
304{
305  gdbarch_register_osabi (bfd_arch_m68k, 0, GDB_OSABI_LINUX,
306			  m68k_linux_init_abi);
307}
308