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