]>
Commit | Line | Data |
---|---|---|
2a75c0b6 | 1 | /* ARM EABI compliant unwinding routines |
83ffe9cd | 2 | Copyright (C) 2004-2023 Free Software Foundation, Inc. |
2a75c0b6 PB |
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 | |
748086b7 | 7 | Free Software Foundation; either version 3, or (at your option) any |
2a75c0b6 PB |
8 | later version. |
9 | ||
2a75c0b6 PB |
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 | ||
748086b7 JJ |
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 | ||
48528842 | 24 | #pragma GCC target ("general-regs-only") |
2a75c0b6 PB |
25 | #include "unwind.h" |
26 | ||
74d9c39f DJ |
27 | /* We add a prototype for abort here to avoid creating a dependency on |
28 | target headers. */ | |
29 | extern void abort (void); | |
30 | ||
2a75c0b6 PB |
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 | |
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 | |
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 | |
105 | __gnu_unwind_execute (_Unwind_Context * context, __gnu_unwind_state * uws) | |
106 | { | |
107 | _uw op; | |
108 | int set_pc; | |
3dba5b2c | 109 | #if defined(TARGET_HAVE_PACBTI) |
7161afc7 | 110 | int set_pac = 0; |
55a2d809 | 111 | int set_pac_sp = 0; |
3dba5b2c | 112 | #endif |
2a75c0b6 | 113 | _uw reg; |
55a2d809 | 114 | _uw sp; |
2a75c0b6 PB |
115 | |
116 | set_pc = 0; | |
117 | for (;;) | |
118 | { | |
119 | op = next_unwind_byte (uws); | |
120 | if (op == CODE_FINISH) | |
121 | { | |
7161afc7 AC |
122 | /* When we reach end, we have to authenticate R12 we just popped |
123 | earlier. | |
124 | ||
125 | Note: while the check provides additional security against a | |
126 | corrupted unwind chain, it isn't essential for correct unwinding | |
127 | of an uncorrupted chain. */ | |
128 | #if defined(TARGET_HAVE_PACBTI) | |
129 | if (set_pac) | |
130 | { | |
7161afc7 AC |
131 | _uw lr; |
132 | _uw pac; | |
55a2d809 SP |
133 | if (!set_pac_sp) |
134 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, | |
135 | &sp); | |
7161afc7 AC |
136 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32, &lr); |
137 | _Unwind_VRS_Get (context, _UVRSC_PAC, R_IP, | |
138 | _UVRSD_UINT32, &pac); | |
139 | __asm__ __volatile__ | |
140 | ("autg %0, %1, %2" : : "r"(pac), "r"(lr), "r"(sp) :); | |
141 | } | |
142 | #endif | |
143 | ||
2a75c0b6 PB |
144 | /* If we haven't already set pc then copy it from lr. */ |
145 | if (!set_pc) | |
146 | { | |
147 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32, | |
148 | ®); | |
149 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, | |
150 | ®); | |
151 | set_pc = 1; | |
152 | } | |
153 | /* Drop out of the loop. */ | |
154 | break; | |
155 | } | |
156 | if ((op & 0x80) == 0) | |
157 | { | |
158 | /* vsp = vsp +- (imm6 << 2 + 4). */ | |
159 | _uw offset; | |
160 | ||
161 | offset = ((op & 0x3f) << 2) + 4; | |
162 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
163 | if (op & 0x40) | |
164 | reg -= offset; | |
165 | else | |
166 | reg += offset; | |
167 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
168 | continue; | |
169 | } | |
170 | ||
171 | if ((op & 0xf0) == 0x80) | |
172 | { | |
173 | op = (op << 8) | next_unwind_byte (uws); | |
174 | if (op == 0x8000) | |
175 | { | |
176 | /* Refuse to unwind. */ | |
177 | return _URC_FAILURE; | |
178 | } | |
179 | /* Pop r4-r15 under mask. */ | |
180 | op = (op << 4) & 0xfff0; | |
181 | if (_Unwind_VRS_Pop (context, _UVRSC_CORE, op, _UVRSD_UINT32) | |
182 | != _UVRSR_OK) | |
183 | return _URC_FAILURE; | |
184 | if (op & (1 << R_PC)) | |
185 | set_pc = 1; | |
186 | continue; | |
187 | } | |
188 | if ((op & 0xf0) == 0x90) | |
189 | { | |
190 | op &= 0xf; | |
191 | if (op == 13 || op == 15) | |
192 | /* Reserved. */ | |
193 | return _URC_FAILURE; | |
194 | /* vsp = r[nnnn]. */ | |
195 | _Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, ®); | |
196 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
197 | continue; | |
198 | } | |
199 | if ((op & 0xf0) == 0xa0) | |
200 | { | |
201 | /* Pop r4-r[4+nnn], [lr]. */ | |
202 | _uw mask; | |
203 | ||
204 | mask = (0xff0 >> (7 - (op & 7))) & 0xff0; | |
205 | if (op & 8) | |
206 | mask |= (1 << R_LR); | |
207 | if (_Unwind_VRS_Pop (context, _UVRSC_CORE, mask, _UVRSD_UINT32) | |
208 | != _UVRSR_OK) | |
209 | return _URC_FAILURE; | |
210 | continue; | |
211 | } | |
212 | if ((op & 0xf0) == 0xb0) | |
213 | { | |
214 | /* op == 0xb0 already handled. */ | |
215 | if (op == 0xb1) | |
216 | { | |
217 | op = next_unwind_byte (uws); | |
218 | if (op == 0 || ((op & 0xf0) != 0)) | |
219 | /* Spare. */ | |
220 | return _URC_FAILURE; | |
221 | /* Pop r0-r4 under mask. */ | |
222 | if (_Unwind_VRS_Pop (context, _UVRSC_CORE, op, _UVRSD_UINT32) | |
223 | != _UVRSR_OK) | |
224 | return _URC_FAILURE; | |
225 | continue; | |
226 | } | |
227 | if (op == 0xb2) | |
228 | { | |
229 | /* vsp = vsp + 0x204 + (uleb128 << 2). */ | |
230 | int shift; | |
231 | ||
232 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, | |
233 | ®); | |
234 | op = next_unwind_byte (uws); | |
235 | shift = 2; | |
236 | while (op & 0x80) | |
237 | { | |
238 | reg += ((op & 0x7f) << shift); | |
239 | shift += 7; | |
240 | op = next_unwind_byte (uws); | |
241 | } | |
242 | reg += ((op & 0x7f) << shift) + 0x204; | |
243 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, | |
244 | ®); | |
245 | continue; | |
246 | } | |
247 | if (op == 0xb3) | |
248 | { | |
249 | /* Pop VFP registers with fldmx. */ | |
250 | op = next_unwind_byte (uws); | |
08a557f6 | 251 | op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); |
2a75c0b6 PB |
252 | if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_VFPX) |
253 | != _UVRSR_OK) | |
254 | return _URC_FAILURE; | |
255 | continue; | |
256 | } | |
7161afc7 AC |
257 | /* Pop PAC off the stack into VRS pseudo.pac. */ |
258 | if (op == 0xb4) | |
259 | { | |
260 | if (_Unwind_VRS_Pop (context, _UVRSC_PAC, 0, _UVRSD_UINT32) | |
261 | != _UVRSR_OK) | |
262 | return _URC_FAILURE; | |
3dba5b2c | 263 | #if defined(TARGET_HAVE_PACBTI) |
7161afc7 | 264 | set_pac = 1; |
3dba5b2c | 265 | #endif |
7161afc7 AC |
266 | continue; |
267 | } | |
268 | ||
55a2d809 SP |
269 | /* Use current VSP as modifier in PAC validation. */ |
270 | if (op == 0xb5) | |
271 | { | |
272 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &sp); | |
3dba5b2c | 273 | #if defined(TARGET_HAVE_PACBTI) |
55a2d809 | 274 | set_pac_sp = 1; |
3dba5b2c | 275 | #endif |
55a2d809 SP |
276 | continue; |
277 | } | |
278 | ||
5005fe22 RE |
279 | if ((op & 0xfc) == 0xb4) /* Obsolete FPA. */ |
280 | return _URC_FAILURE; | |
281 | ||
2a75c0b6 PB |
282 | /* op & 0xf8 == 0xb8. */ |
283 | /* Pop VFP D[8]-D[8+nnn] with fldmx. */ | |
284 | op = 0x80000 | ((op & 7) + 1); | |
285 | if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_VFPX) | |
286 | != _UVRSR_OK) | |
287 | return _URC_FAILURE; | |
288 | continue; | |
289 | } | |
290 | if ((op & 0xf0) == 0xc0) | |
291 | { | |
292 | if (op == 0xc6) | |
293 | { | |
294 | /* Pop iWMMXt D registers. */ | |
295 | op = next_unwind_byte (uws); | |
08a557f6 | 296 | op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); |
2a75c0b6 PB |
297 | if (_Unwind_VRS_Pop (context, _UVRSC_WMMXD, op, _UVRSD_UINT64) |
298 | != _UVRSR_OK) | |
299 | return _URC_FAILURE; | |
300 | continue; | |
301 | } | |
302 | if (op == 0xc7) | |
303 | { | |
304 | op = next_unwind_byte (uws); | |
305 | if (op == 0 || (op & 0xf0) != 0) | |
306 | /* Spare. */ | |
307 | return _URC_FAILURE; | |
308 | /* Pop iWMMXt wCGR{3,2,1,0} under mask. */ | |
309 | if (_Unwind_VRS_Pop (context, _UVRSC_WMMXC, op, _UVRSD_UINT32) | |
310 | != _UVRSR_OK) | |
311 | return _URC_FAILURE; | |
312 | continue; | |
313 | } | |
314 | if ((op & 0xf8) == 0xc0) | |
315 | { | |
316 | /* Pop iWMMXt wR[10]-wR[10+nnn]. */ | |
317 | op = 0xa0000 | ((op & 0xf) + 1); | |
318 | if (_Unwind_VRS_Pop (context, _UVRSC_WMMXD, op, _UVRSD_UINT64) | |
319 | != _UVRSR_OK) | |
320 | return _URC_FAILURE; | |
321 | continue; | |
322 | } | |
323 | if (op == 0xc8) | |
324 | { | |
8edfc4cc MS |
325 | /* Pop VFPv3 registers D[16+ssss]-D[16+ssss+cccc] with vldm. */ |
326 | op = next_unwind_byte (uws); | |
327 | op = (((op & 0xf0) + 16) << 12) | ((op & 0xf) + 1); | |
328 | if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_DOUBLE) | |
329 | != _UVRSR_OK) | |
330 | return _URC_FAILURE; | |
331 | continue; | |
2a75c0b6 PB |
332 | } |
333 | if (op == 0xc9) | |
334 | { | |
335 | /* Pop VFP registers with fldmd. */ | |
336 | op = next_unwind_byte (uws); | |
08a557f6 | 337 | op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); |
2a75c0b6 PB |
338 | if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_DOUBLE) |
339 | != _UVRSR_OK) | |
340 | return _URC_FAILURE; | |
341 | continue; | |
342 | } | |
343 | /* Spare. */ | |
344 | return _URC_FAILURE; | |
345 | } | |
346 | if ((op & 0xf8) == 0xd0) | |
347 | { | |
348 | /* Pop VFP D[8]-D[8+nnn] with fldmd. */ | |
349 | op = 0x80000 | ((op & 7) + 1); | |
350 | if (_Unwind_VRS_Pop (context, _UVRSC_VFP, op, _UVRSD_DOUBLE) | |
351 | != _UVRSR_OK) | |
352 | return _URC_FAILURE; | |
353 | continue; | |
354 | } | |
355 | /* Spare. */ | |
356 | return _URC_FAILURE; | |
357 | } | |
358 | return _URC_OK; | |
359 | } | |
360 | ||
361 | ||
362 | /* Execute the unwinding instructions associated with a frame. UCBP and | |
363 | CONTEXT are the current exception object and virtual CPU state | |
364 | respectively. */ | |
365 | ||
366 | _Unwind_Reason_Code | |
367 | __gnu_unwind_frame (_Unwind_Control_Block * ucbp, _Unwind_Context * context) | |
368 | { | |
369 | _uw *ptr; | |
370 | __gnu_unwind_state uws; | |
371 | ||
372 | ptr = (_uw *) ucbp->pr_cache.ehtp; | |
373 | /* Skip over the personality routine address. */ | |
374 | ptr++; | |
375 | /* Setup the unwinder state. */ | |
376 | uws.data = (*ptr) << 8; | |
377 | uws.next = ptr + 1; | |
378 | uws.bytes_left = 3; | |
379 | uws.words_left = ((*ptr) >> 24) & 0xff; | |
380 | ||
381 | return __gnu_unwind_execute (context, &uws); | |
382 | } | |
383 | ||
384 | /* Get the _Unwind_Control_Block from an _Unwind_Context. */ | |
385 | ||
386 | static inline _Unwind_Control_Block * | |
387 | unwind_UCB_from_context (_Unwind_Context * context) | |
388 | { | |
389 | return (_Unwind_Control_Block *) _Unwind_GetGR (context, R_IP); | |
390 | } | |
391 | ||
392 | /* Get the start address of the function being unwound. */ | |
393 | ||
394 | _Unwind_Ptr | |
395 | _Unwind_GetRegionStart (_Unwind_Context * context) | |
396 | { | |
397 | _Unwind_Control_Block *ucbp; | |
398 | ||
399 | ucbp = unwind_UCB_from_context (context); | |
400 | return (_Unwind_Ptr) ucbp->pr_cache.fnstart; | |
401 | } | |
402 | ||
403 | /* Find the Language specific exception data. */ | |
404 | ||
405 | void * | |
406 | _Unwind_GetLanguageSpecificData (_Unwind_Context * context) | |
407 | { | |
408 | _Unwind_Control_Block *ucbp; | |
409 | _uw *ptr; | |
410 | ||
411 | /* Get a pointer to the exception table entry. */ | |
412 | ucbp = unwind_UCB_from_context (context); | |
413 | ptr = (_uw *) ucbp->pr_cache.ehtp; | |
414 | /* Skip the personality routine address. */ | |
415 | ptr++; | |
416 | /* Skip the unwind opcodes. */ | |
417 | ptr += (((*ptr) >> 24) & 0xff) + 1; | |
418 | ||
419 | return ptr; | |
420 | } | |
421 | ||
66403286 PB |
422 | |
423 | /* These two should never be used. */ | |
424 | ||
425 | _Unwind_Ptr | |
426 | _Unwind_GetDataRelBase (_Unwind_Context *context __attribute__ ((unused))) | |
427 | { | |
428 | abort (); | |
429 | } | |
430 | ||
431 | _Unwind_Ptr | |
432 | _Unwind_GetTextRelBase (_Unwind_Context *context __attribute__ ((unused))) | |
433 | { | |
434 | abort (); | |
435 | } |