1/* DWARF2 EH unwinding support for Linux/m68k.
2   Copyright (C) 2006-2020 Free Software Foundation, Inc.
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 3, or (at your option)
9any later version.
10
11GCC is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16Under Section 7 of GPL version 3, you are granted additional
17permissions described in the GCC Runtime Library Exception, version
183.1, as published by the Free Software Foundation.
19
20You should have received a copy of the GNU General Public License and
21a copy of the GCC Runtime Library Exception along with this program;
22see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23<http://www.gnu.org/licenses/>.  */
24
25/* Do code reading to identify a signal frame, and set the frame
26   state data appropriately.  See unwind-dw2.c for the structs.
27   Don't use this at all if inhibit_libc is used.  */
28
29#ifndef inhibit_libc
30
31#include <signal.h>
32
33/* <sys/ucontext.h> is unfortunately broken right now.  */
34struct uw_ucontext {
35	unsigned long	  uc_flags;
36	ucontext_t	 *uc_link;
37	stack_t		  uc_stack;
38	mcontext_t	  uc_mcontext;
39	unsigned long	  uc_filler[80];
40	sigset_t	  uc_sigmask;
41};
42
43#define MD_FALLBACK_FRAME_STATE_FOR m68k_fallback_frame_state
44
45#ifdef __mcoldfire__
46#define M68K_FP_SIZE  8
47#else
48#define M68K_FP_SIZE  12
49#endif
50
51static _Unwind_Reason_Code
52m68k_fallback_frame_state (struct _Unwind_Context *context,
53			   _Unwind_FrameState *fs)
54{
55  unsigned short *pc = context->ra;
56  long cfa;
57
58  /* moveq #__NR_sigreturn,%d0; trap #0  */
59  if (pc[0] == 0x7077 && pc[1] == 0x4e40)
60    {
61      struct sigcontext *sc;
62
63      /* Context is passed as the 3rd argument.  */
64      sc = *(struct sigcontext **) (context->cfa + 8);
65
66      cfa = sc->sc_usp;
67      fs->regs.cfa_how = CFA_REG_OFFSET;
68      fs->regs.cfa_reg = 15;
69      fs->regs.cfa_offset = cfa - (long) context->cfa;
70
71      fs->regs.reg[0].how = REG_SAVED_OFFSET;
72      fs->regs.reg[0].loc.offset = (long) &sc->sc_d0 - cfa;
73      fs->regs.reg[1].how = REG_SAVED_OFFSET;
74      fs->regs.reg[1].loc.offset = (long) &sc->sc_d1 - cfa;
75      fs->regs.reg[8].how = REG_SAVED_OFFSET;
76      fs->regs.reg[8].loc.offset = (long) &sc->sc_a0 - cfa;
77      fs->regs.reg[9].how = REG_SAVED_OFFSET;
78      fs->regs.reg[9].loc.offset = (long) &sc->sc_a1 - cfa;
79
80#ifdef __uClinux__
81      fs->regs.reg[13].how = REG_SAVED_OFFSET;
82      fs->regs.reg[13].loc.offset = (long) &sc->sc_a5 - cfa;
83#endif
84
85      fs->regs.reg[24].how = REG_SAVED_OFFSET;
86      fs->regs.reg[24].loc.offset = (long) &sc->sc_pc - cfa;
87
88#ifndef __uClinux__
89      if (*(int *) sc->sc_fpstate)
90	{
91	  int *fpregs = (int *) sc->sc_fpregs;
92
93	  fs->regs.reg[16].how = REG_SAVED_OFFSET;
94	  fs->regs.reg[16].loc.offset = (long) &fpregs[0] - cfa;
95	  fs->regs.reg[17].how = REG_SAVED_OFFSET;
96	  fs->regs.reg[17].loc.offset = (long) &fpregs[M68K_FP_SIZE/4] - cfa;
97	}
98#elif defined __mcffpu__
99# error Implement this when uClinux kernel is ported to an FPU architecture
100#endif
101    }
102#ifdef __mcoldfire__
103  /* move.l #__NR_rt_sigreturn,%d0; trap #0 */
104  else if (pc[0] == 0x203c && pc[1] == 0x0000 &&
105	   pc[2] == 0x00ad && pc[3] == 0x4e40)
106#else
107  /* moveq #~__NR_rt_sigreturn,%d0; not.b %d0; trap #0 */
108  else if (pc[0] == 0x7052 && pc[1] == 0x4600 && pc[2] == 0x4e40)
109#endif
110    {
111      struct uw_ucontext *uc;
112      greg_t *gregs;
113      int i;
114
115      /* Context is passed as the 3rd argument.  */
116      uc = *(struct uw_ucontext **) (context->cfa + 8);
117
118      gregs = uc->uc_mcontext.gregs;
119      cfa = gregs[15];
120      fs->regs.cfa_how = CFA_REG_OFFSET;
121      fs->regs.cfa_reg = 15;
122      fs->regs.cfa_offset = cfa - (long) context->cfa;
123
124      /* register %d0-%d7/%a0-%a6  */
125      for (i = 0; i <= 14; i++)
126	{
127	  fs->regs.reg[i].how = REG_SAVED_OFFSET;
128	  fs->regs.reg[i].loc.offset = (long) &gregs[i] - cfa;
129	}
130
131      /* return address  */
132      fs->regs.reg[24].how = REG_SAVED_OFFSET;
133      fs->regs.reg[24].loc.offset = (long) &gregs[16] - cfa;
134
135#define uc_fpstate      uc_filler[0]
136
137      if (uc->uc_fpstate)
138	{
139	  long fpregs = (long) uc->uc_mcontext.fpregs.f_fpregs;
140
141	  /* register %fp0-%fp7  */
142	  for (i = 16; i <= 23; i++)
143	    {
144	      fs->regs.reg[i].how = REG_SAVED_OFFSET;
145	      fs->regs.reg[i].loc.offset = fpregs - cfa;
146	      fpregs += M68K_FP_SIZE;
147	    }
148	}
149    }
150  else
151    return _URC_END_OF_STACK;
152
153  fs->retaddr_column = 24;
154  fs->signal_frame = 1;
155
156  return _URC_NO_REASON;
157}
158#endif /* ifdef inhibit_libc  */
159