1/* ARM EABI compliant unwinding routines
2   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
3   Contributed by Paul Brook
4
5   This file is free software; you can redistribute it and/or modify it
6   under the terms of the GNU General Public License as published by the
7   Free Software Foundation; either version 2, or (at your option) any
8   later version.
9
10   In addition to the permissions in the GNU General Public License, the
11   Free Software Foundation gives you unlimited permission to link the
12   compiled version of this file into combinations with other programs,
13   and to distribute those combinations without any restriction coming
14   from the use of this file.  (The General Public License restrictions
15   do apply in other respects; for example, they cover modification of
16   the file, and distribution when not linked into a combine
17   executable.)
18
19   This file is distributed in the hope that it will be useful, but
20   WITHOUT ANY WARRANTY; without even the implied warranty of
21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22   General Public License for more details.
23
24   You should have received a copy of the GNU General Public License
25   along with this program; see the file COPYING.  If not, write to
26   the Free Software Foundation, 51 Franklin Street, Fifth Floor,
27   Boston, MA 02110-1301, USA.  */
28
29/****************************************************************************
30 * The functions here are derived from gcc/config/arm/pr-support.c from the
31 * 4.3.x release. The main changes here involve the use of ptrace to retrieve
32 * memory/processor states from a remote process.
33 ****************************************************************************/
34
35#include <sys/types.h>
36#include <unwind.h>
37
38#include "utility.h"
39
40/* We add a prototype for abort here to avoid creating a dependency on
41   target headers.  */
42extern void abort (void);
43
44/* Derived from _Unwind_VRS_Pop to use ptrace */
45extern _Unwind_VRS_Result
46unwind_VRS_Pop_with_ptrace (_Unwind_Context *context,
47                            _Unwind_VRS_RegClass regclass,
48                            _uw discriminator,
49                            _Unwind_VRS_DataRepresentation representation,
50                            pid_t pid);
51
52typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
53
54/* Misc constants.  */
55#define R_IP    12
56#define R_SP    13
57#define R_LR    14
58#define R_PC    15
59
60#define uint32_highbit (((_uw) 1) << 31)
61
62void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
63
64/* Unwind descriptors.  */
65
66typedef struct
67{
68  _uw16 length;
69  _uw16 offset;
70} EHT16;
71
72typedef struct
73{
74  _uw length;
75  _uw offset;
76} EHT32;
77
78/* Personality routine helper functions.  */
79
80#define CODE_FINISH (0xb0)
81
82/* Derived from next_unwind_byte to use ptrace */
83/* Return the next byte of unwinding information, or CODE_FINISH if there is
84   no data remaining.  */
85static inline _uw8
86next_unwind_byte_with_ptrace (__gnu_unwind_state * uws, pid_t pid)
87{
88  _uw8 b;
89
90  if (uws->bytes_left == 0)
91    {
92      /* Load another word */
93      if (uws->words_left == 0)
94	return CODE_FINISH; /* Nothing left.  */
95      uws->words_left--;
96      uws->data = get_remote_word(pid, uws->next);
97      uws->next++;
98      uws->bytes_left = 3;
99    }
100  else
101    uws->bytes_left--;
102
103  /* Extract the most significant byte.  */
104  b = (uws->data >> 24) & 0xff;
105  uws->data <<= 8;
106  return b;
107}
108
109/* Execute the unwinding instructions described by UWS.  */
110_Unwind_Reason_Code
111unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws,
112                           pid_t pid)
113{
114  _uw op;
115  int set_pc;
116  _uw reg;
117
118  set_pc = 0;
119  for (;;)
120    {
121      op = next_unwind_byte_with_ptrace (uws, pid);
122      if (op == CODE_FINISH)
123	{
124	  /* If we haven't already set pc then copy it from lr.  */
125	  if (!set_pc)
126	    {
127	      _Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32,
128			       &reg);
129	      _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32,
130			       &reg);
131	      set_pc = 1;
132	    }
133	  /* Drop out of the loop.  */
134	  break;
135	}
136      if ((op & 0x80) == 0)
137	{
138	  /* vsp = vsp +- (imm6 << 2 + 4).  */
139	  _uw offset;
140
141	  offset = ((op & 0x3f) << 2) + 4;
142	  _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
143	  if (op & 0x40)
144	    reg -= offset;
145	  else
146	    reg += offset;
147	  _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
148	  continue;
149	}
150
151      if ((op & 0xf0) == 0x80)
152	{
153	  op = (op << 8) | next_unwind_byte_with_ptrace (uws, pid);
154	  if (op == 0x8000)
155	    {
156	      /* Refuse to unwind.  */
157	      return _URC_FAILURE;
158	    }
159	  /* Pop r4-r15 under mask.  */
160	  op = (op << 4) & 0xfff0;
161	  if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, _UVRSD_UINT32,
162                                      pid)
163	      != _UVRSR_OK)
164	    return _URC_FAILURE;
165	  if (op & (1 << R_PC))
166	    set_pc = 1;
167	  continue;
168	}
169      if ((op & 0xf0) == 0x90)
170	{
171	  op &= 0xf;
172	  if (op == 13 || op == 15)
173	    /* Reserved.  */
174	    return _URC_FAILURE;
175	  /* vsp = r[nnnn].  */
176	  _Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, &reg);
177	  _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
178	  continue;
179	}
180      if ((op & 0xf0) == 0xa0)
181	{
182	  /* Pop r4-r[4+nnn], [lr].  */
183	  _uw mask;
184
185	  mask = (0xff0 >> (7 - (op & 7))) & 0xff0;
186	  if (op & 8)
187	    mask |= (1 << R_LR);
188	  if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, mask, _UVRSD_UINT32,
189                                      pid)
190	      != _UVRSR_OK)
191	    return _URC_FAILURE;
192	  continue;
193	}
194      if ((op & 0xf0) == 0xb0)
195	{
196	  /* op == 0xb0 already handled.  */
197	  if (op == 0xb1)
198	    {
199	      op = next_unwind_byte_with_ptrace (uws, pid);
200	      if (op == 0 || ((op & 0xf0) != 0))
201		/* Spare.  */
202		return _URC_FAILURE;
203	      /* Pop r0-r4 under mask.  */
204	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op,
205                                          _UVRSD_UINT32, pid)
206		  != _UVRSR_OK)
207		return _URC_FAILURE;
208	      continue;
209	    }
210	  if (op == 0xb2)
211	    {
212	      /* vsp = vsp + 0x204 + (uleb128 << 2).  */
213	      int shift;
214
215	      _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
216			       &reg);
217	      op = next_unwind_byte_with_ptrace (uws, pid);
218	      shift = 2;
219	      while (op & 0x80)
220		{
221		  reg += ((op & 0x7f) << shift);
222		  shift += 7;
223		  op = next_unwind_byte_with_ptrace (uws, pid);
224		}
225	      reg += ((op & 0x7f) << shift) + 0x204;
226	      _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
227			       &reg);
228	      continue;
229	    }
230	  if (op == 0xb3)
231	    {
232	      /* Pop VFP registers with fldmx.  */
233	      op = next_unwind_byte_with_ptrace (uws, pid);
234	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
235	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX,
236                                          pid)
237		  != _UVRSR_OK)
238		return _URC_FAILURE;
239	      continue;
240	    }
241	  if ((op & 0xfc) == 0xb4)
242	    {
243	      /* Pop FPA E[4]-E[4+nn].  */
244	      op = 0x40000 | ((op & 3) + 1);
245	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX,
246                                          pid)
247		  != _UVRSR_OK)
248		return _URC_FAILURE;
249	      continue;
250	    }
251	  /* op & 0xf8 == 0xb8.  */
252	  /* Pop VFP D[8]-D[8+nnn] with fldmx.  */
253	  op = 0x80000 | ((op & 7) + 1);
254	  if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, pid)
255	      != _UVRSR_OK)
256	    return _URC_FAILURE;
257	  continue;
258	}
259      if ((op & 0xf0) == 0xc0)
260	{
261	  if (op == 0xc6)
262	    {
263	      /* Pop iWMMXt D registers.  */
264	      op = next_unwind_byte_with_ptrace (uws, pid);
265	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
266	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op,
267                                          _UVRSD_UINT64, pid)
268		  != _UVRSR_OK)
269		return _URC_FAILURE;
270	      continue;
271	    }
272	  if (op == 0xc7)
273	    {
274	      op = next_unwind_byte_with_ptrace (uws, pid);
275	      if (op == 0 || (op & 0xf0) != 0)
276		/* Spare.  */
277		return _URC_FAILURE;
278	      /* Pop iWMMXt wCGR{3,2,1,0} under mask.  */
279	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXC, op,
280                                          _UVRSD_UINT32, pid)
281		  != _UVRSR_OK)
282		return _URC_FAILURE;
283	      continue;
284	    }
285	  if ((op & 0xf8) == 0xc0)
286	    {
287	      /* Pop iWMMXt wR[10]-wR[10+nnn].  */
288	      op = 0xa0000 | ((op & 0xf) + 1);
289	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op,
290                                          _UVRSD_UINT64, pid)
291		  != _UVRSR_OK)
292		return _URC_FAILURE;
293	      continue;
294	    }
295	  if (op == 0xc8)
296	    {
297#ifndef __VFP_FP__
298 	      /* Pop FPA registers.  */
299 	      op = next_unwind_byte_with_ptrace (uws, pid);
300	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
301 	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX,
302                                          pid)
303 		  != _UVRSR_OK)
304 		return _URC_FAILURE;
305 	      continue;
306#else
307              /* Pop VFPv3 registers D[16+ssss]-D[16+ssss+cccc] with vldm.  */
308              op = next_unwind_byte_with_ptrace (uws, pid);
309              op = (((op & 0xf0) + 16) << 12) | ((op & 0xf) + 1);
310              if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op,
311                                              _UVRSD_DOUBLE, pid)
312                  != _UVRSR_OK)
313                return _URC_FAILURE;
314              continue;
315#endif
316	    }
317	  if (op == 0xc9)
318	    {
319	      /* Pop VFP registers with fldmd.  */
320	      op = next_unwind_byte_with_ptrace (uws, pid);
321	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
322	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op,
323                                          _UVRSD_DOUBLE, pid)
324		  != _UVRSR_OK)
325		return _URC_FAILURE;
326	      continue;
327	    }
328	  /* Spare.  */
329	  return _URC_FAILURE;
330	}
331      if ((op & 0xf8) == 0xd0)
332	{
333	  /* Pop VFP D[8]-D[8+nnn] with fldmd.  */
334	  op = 0x80000 | ((op & 7) + 1);
335	  if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_DOUBLE,
336                                      pid)
337	      != _UVRSR_OK)
338	    return _URC_FAILURE;
339	  continue;
340	}
341      /* Spare.  */
342      return _URC_FAILURE;
343    }
344  return _URC_OK;
345}
346