xref: /openbsd-src/gnu/gcc/gcc/config/arm/pr-support.c (revision 404b540a9034ac75a6199ad1a32d1bbc7a0d4210)
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 #include "unwind.h"
29 
30 /* We add a prototype for abort here to avoid creating a dependency on
31    target headers.  */
32 extern void abort (void);
33 
34 typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
35 
36 /* Misc constants.  */
37 #define R_IP    12
38 #define R_SP    13
39 #define R_LR    14
40 #define R_PC    15
41 
42 #define uint32_highbit (((_uw) 1) << 31)
43 
44 void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
45 
46 /* Unwind descriptors.  */
47 
48 typedef struct
49 {
50   _uw16 length;
51   _uw16 offset;
52 } EHT16;
53 
54 typedef struct
55 {
56   _uw length;
57   _uw offset;
58 } EHT32;
59 
60 /* Calculate the address encoded by a 31-bit self-relative offset at address
61    P.  Copy of routine in unwind-arm.c.  */
62 
63 static inline _uw
selfrel_offset31(const _uw * p)64 selfrel_offset31 (const _uw *p)
65 {
66   _uw offset;
67 
68   offset = *p;
69   /* Sign extend to 32 bits.  */
70   if (offset & (1 << 30))
71     offset |= 1u << 31;
72 
73   return offset + (_uw) p;
74 }
75 
76 
77 /* Personality routine helper functions.  */
78 
79 #define CODE_FINISH (0xb0)
80 
81 /* Return the next byte of unwinding information, or CODE_FINISH if there is
82    no data remaining.  */
83 static inline _uw8
next_unwind_byte(__gnu_unwind_state * uws)84 next_unwind_byte (__gnu_unwind_state * uws)
85 {
86   _uw8 b;
87 
88   if (uws->bytes_left == 0)
89     {
90       /* Load another word */
91       if (uws->words_left == 0)
92 	return CODE_FINISH; /* Nothing left.  */
93       uws->words_left--;
94       uws->data = *(uws->next++);
95       uws->bytes_left = 3;
96     }
97   else
98     uws->bytes_left--;
99 
100   /* Extract the most significant byte.  */
101   b = (uws->data >> 24) & 0xff;
102   uws->data <<= 8;
103   return b;
104 }
105 
106 /* Execute the unwinding instructions described by UWS.  */
107 _Unwind_Reason_Code
__gnu_unwind_execute(_Unwind_Context * context,__gnu_unwind_state * uws)108 __gnu_unwind_execute (_Unwind_Context * context, __gnu_unwind_state * uws)
109 {
110   _uw op;
111   int set_pc;
112   _uw reg;
113 
114   set_pc = 0;
115   for (;;)
116     {
117       op = next_unwind_byte (uws);
118       if (op == CODE_FINISH)
119 	{
120 	  /* If we haven't already set pc then copy it from lr.  */
121 	  if (!set_pc)
122 	    {
123 	      _Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32,
124 			       &reg);
125 	      _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32,
126 			       &reg);
127 	      set_pc = 1;
128 	    }
129 	  /* Drop out of the loop.  */
130 	  break;
131 	}
132       if ((op & 0x80) == 0)
133 	{
134 	  /* vsp = vsp +- (imm6 << 2 + 4).  */
135 	  _uw offset;
136 
137 	  offset = ((op & 0x3f) << 2) + 4;
138 	  _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
139 	  if (op & 0x40)
140 	    reg -= offset;
141 	  else
142 	    reg += offset;
143 	  _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
144 	  continue;
145 	}
146 
147       if ((op & 0xf0) == 0x80)
148 	{
149 	  op = (op << 8) | next_unwind_byte (uws);
150 	  if (op == 0x8000)
151 	    {
152 	      /* Refuse to unwind.  */
153 	      return _URC_FAILURE;
154 	    }
155 	  /* Pop r4-r15 under mask.  */
156 	  op = (op << 4) & 0xfff0;
157 	  if (_Unwind_VRS_Pop (context, _UVRSC_CORE, op, _UVRSD_UINT32)
158 	      != _UVRSR_OK)
159 	    return _URC_FAILURE;
160 	  if (op & (1 << R_PC))
161 	    set_pc = 1;
162 	  continue;
163 	}
164       if ((op & 0xf0) == 0x90)
165 	{
166 	  op &= 0xf;
167 	  if (op == 13 || op == 15)
168 	    /* Reserved.  */
169 	    return _URC_FAILURE;
170 	  /* vsp = r[nnnn].  */
171 	  _Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, &reg);
172 	  _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
173 	  continue;
174 	}
175       if ((op & 0xf0) == 0xa0)
176 	{
177 	  /* Pop r4-r[4+nnn], [lr].  */
178 	  _uw mask;
179 
180 	  mask = (0xff0 >> (7 - (op & 7))) & 0xff0;
181 	  if (op & 8)
182 	    mask |= (1 << R_LR);
183 	  if (_Unwind_VRS_Pop (context, _UVRSC_CORE, mask, _UVRSD_UINT32)
184 	      != _UVRSR_OK)
185 	    return _URC_FAILURE;
186 	  continue;
187 	}
188       if ((op & 0xf0) == 0xb0)
189 	{
190 	  /* op == 0xb0 already handled.  */
191 	  if (op == 0xb1)
192 	    {
193 	      op = next_unwind_byte (uws);
194 	      if (op == 0 || ((op & 0xf0) != 0))
195 		/* Spare.  */
196 		return _URC_FAILURE;
197 	      /* Pop r0-r4 under mask.  */
198 	      if (_Unwind_VRS_Pop (context, _UVRSC_CORE, op, _UVRSD_UINT32)
199 		  != _UVRSR_OK)
200 		return _URC_FAILURE;
201 	      continue;
202 	    }
203 	  if (op == 0xb2)
204 	    {
205 	      /* vsp = vsp + 0x204 + (uleb128 << 2).  */
206 	      int shift;
207 
208 	      _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
209 			       &reg);
210 	      op = next_unwind_byte (uws);
211 	      shift = 2;
212 	      while (op & 0x80)
213 		{
214 		  reg += ((op & 0x7f) << shift);
215 		  shift += 7;
216 		  op = next_unwind_byte (uws);
217 		}
218 	      reg += ((op & 0x7f) << shift) + 0x204;
219 	      _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
220 			       &reg);
221 	      continue;
222 	    }
223 	  if (op == 0xb3)
224 	    {
225 	      /* Pop VFP registers with fldmx.  */
226 	      op = next_unwind_byte (uws);
227 	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
228 	      if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_VFPX)
229 		  != _UVRSR_OK)
230 		return _URC_FAILURE;
231 	      continue;
232 	    }
233 	  if ((op & 0xfc) == 0xb4)
234 	    {
235 	      /* Pop FPA E[4]-E[4+nn].  */
236 	      op = 0x40000 | ((op & 3) + 1);
237 	      if (_Unwind_VRS_Pop (context, _UVRSC_FPA, op, _UVRSD_FPAX)
238 		  != _UVRSR_OK)
239 		return _URC_FAILURE;
240 	      continue;
241 	    }
242 	  /* op & 0xf8 == 0xb8.  */
243 	  /* Pop VFP D[8]-D[8+nnn] with fldmx.  */
244 	  op = 0x80000 | ((op & 7) + 1);
245 	  if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_VFPX)
246 	      != _UVRSR_OK)
247 	    return _URC_FAILURE;
248 	  continue;
249 	}
250       if ((op & 0xf0) == 0xc0)
251 	{
252 	  if (op == 0xc6)
253 	    {
254 	      /* Pop iWMMXt D registers.  */
255 	      op = next_unwind_byte (uws);
256 	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
257 	      if (_Unwind_VRS_Pop (context, _UVRSC_WMMXD, op, _UVRSD_UINT64)
258 		  != _UVRSR_OK)
259 		return _URC_FAILURE;
260 	      continue;
261 	    }
262 	  if (op == 0xc7)
263 	    {
264 	      op = next_unwind_byte (uws);
265 	      if (op == 0 || (op & 0xf0) != 0)
266 		/* Spare.  */
267 		return _URC_FAILURE;
268 	      /* Pop iWMMXt wCGR{3,2,1,0} under mask.  */
269 	      if (_Unwind_VRS_Pop (context, _UVRSC_WMMXC, op, _UVRSD_UINT32)
270 		  != _UVRSR_OK)
271 		return _URC_FAILURE;
272 	      continue;
273 	    }
274 	  if ((op & 0xf8) == 0xc0)
275 	    {
276 	      /* Pop iWMMXt wR[10]-wR[10+nnn].  */
277 	      op = 0xa0000 | ((op & 0xf) + 1);
278 	      if (_Unwind_VRS_Pop (context, _UVRSC_WMMXD, op, _UVRSD_UINT64)
279 		  != _UVRSR_OK)
280 		return _URC_FAILURE;
281 	      continue;
282 	    }
283 	  if (op == 0xc8)
284 	    {
285 	      /* Pop FPA registers.  */
286 	      op = next_unwind_byte (uws);
287 	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
288 	      if (_Unwind_VRS_Pop (context, _UVRSC_FPA, op, _UVRSD_FPAX)
289 		  != _UVRSR_OK)
290 		return _URC_FAILURE;
291 	      continue;
292 	    }
293 	  if (op == 0xc9)
294 	    {
295 	      /* Pop VFP registers with fldmd.  */
296 	      op = next_unwind_byte (uws);
297 	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
298 	      if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_DOUBLE)
299 		  != _UVRSR_OK)
300 		return _URC_FAILURE;
301 	      continue;
302 	    }
303 	  /* Spare.  */
304 	  return _URC_FAILURE;
305 	}
306       if ((op & 0xf8) == 0xd0)
307 	{
308 	  /* Pop VFP D[8]-D[8+nnn] with fldmd.  */
309 	  op = 0x80000 | ((op & 7) + 1);
310 	  if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_DOUBLE)
311 	      != _UVRSR_OK)
312 	    return _URC_FAILURE;
313 	  continue;
314 	}
315       /* Spare.  */
316       return _URC_FAILURE;
317     }
318   return _URC_OK;
319 }
320 
321 
322 /* Execute the unwinding instructions associated with a frame.  UCBP and
323    CONTEXT are the current exception object and virtual CPU state
324    respectively.  */
325 
326 _Unwind_Reason_Code
__gnu_unwind_frame(_Unwind_Control_Block * ucbp,_Unwind_Context * context)327 __gnu_unwind_frame (_Unwind_Control_Block * ucbp, _Unwind_Context * context)
328 {
329   _uw *ptr;
330   __gnu_unwind_state uws;
331 
332   ptr = (_uw *) ucbp->pr_cache.ehtp;
333   /* Skip over the personality routine address.  */
334   ptr++;
335   /* Setup the unwinder state.  */
336   uws.data = (*ptr) << 8;
337   uws.next = ptr + 1;
338   uws.bytes_left = 3;
339   uws.words_left = ((*ptr) >> 24) & 0xff;
340 
341   return __gnu_unwind_execute (context, &uws);
342 }
343 
344 /* Get the _Unwind_Control_Block from an _Unwind_Context.  */
345 
346 static inline _Unwind_Control_Block *
unwind_UCB_from_context(_Unwind_Context * context)347 unwind_UCB_from_context (_Unwind_Context * context)
348 {
349   return (_Unwind_Control_Block *) _Unwind_GetGR (context, R_IP);
350 }
351 
352 /* Get the start address of the function being unwound.  */
353 
354 _Unwind_Ptr
_Unwind_GetRegionStart(_Unwind_Context * context)355 _Unwind_GetRegionStart (_Unwind_Context * context)
356 {
357   _Unwind_Control_Block *ucbp;
358 
359   ucbp = unwind_UCB_from_context (context);
360   return (_Unwind_Ptr) ucbp->pr_cache.fnstart;
361 }
362 
363 /* Find the Language specific exception data.  */
364 
365 void *
_Unwind_GetLanguageSpecificData(_Unwind_Context * context)366 _Unwind_GetLanguageSpecificData (_Unwind_Context * context)
367 {
368   _Unwind_Control_Block *ucbp;
369   _uw *ptr;
370 
371   /* Get a pointer to the exception table entry.  */
372   ucbp = unwind_UCB_from_context (context);
373   ptr = (_uw *) ucbp->pr_cache.ehtp;
374   /* Skip the personality routine address.  */
375   ptr++;
376   /* Skip the unwind opcodes.  */
377   ptr += (((*ptr) >> 24) & 0xff) + 1;
378 
379   return ptr;
380 }
381 
382