]>
Commit | Line | Data |
---|---|---|
1e874273 | 1 | /* C6X ABI compliant unwinding routines |
7adcbafe | 2 | Copyright (C) 2011-2022 Free Software Foundation, Inc. |
1e874273 PB |
3 | |
4 | This file is free software; you can redistribute it and/or modify it | |
5 | under the terms of the GNU General Public License as published by the | |
6 | Free Software Foundation; either version 3, or (at your option) any | |
7 | later version. | |
8 | ||
9 | This file is distributed in the hope that it will be useful, but | |
10 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | General Public License for more details. | |
13 | ||
14 | Under Section 7 of GPL version 3, you are granted additional | |
15 | permissions described in the GCC Runtime Library Exception, version | |
16 | 3.1, as published by the Free Software Foundation. | |
17 | ||
18 | You should have received a copy of the GNU General Public License and | |
19 | a copy of the GCC Runtime Library Exception along with this program; | |
20 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
21 | <http://www.gnu.org/licenses/>. */ | |
22 | ||
23 | #include "unwind.h" | |
24 | ||
25 | /* We add a prototype for abort here to avoid creating a dependency on | |
26 | target headers. */ | |
27 | extern void abort (void); | |
28 | ||
29 | typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ | |
30 | ||
31 | /* Misc constants. */ | |
32 | #define R_A0 0 | |
33 | #define R_A1 1 | |
34 | #define R_A2 2 | |
35 | #define R_A3 3 | |
36 | #define R_A4 4 | |
37 | #define R_A5 5 | |
38 | #define R_A6 6 | |
39 | #define R_A7 7 | |
40 | #define R_A8 8 | |
41 | #define R_A9 9 | |
42 | #define R_A10 10 | |
43 | #define R_A11 11 | |
44 | #define R_A12 12 | |
45 | #define R_A13 13 | |
46 | #define R_A14 14 | |
47 | #define R_A15 15 | |
48 | #define R_B0 16 | |
49 | #define R_B1 17 | |
50 | #define R_B2 18 | |
51 | #define R_B3 19 | |
52 | #define R_B4 20 | |
53 | #define R_B5 21 | |
54 | #define R_B6 22 | |
55 | #define R_B7 23 | |
56 | #define R_B8 24 | |
57 | #define R_B9 25 | |
58 | #define R_B10 26 | |
59 | #define R_B11 27 | |
60 | #define R_B12 28 | |
61 | #define R_B13 29 | |
62 | #define R_B14 30 | |
63 | #define R_B15 31 | |
64 | ||
65 | #define R_SP R_B15 | |
66 | #define R_PC 33 | |
67 | ||
68 | #define uint32_highbit (((_uw) 1) << 31) | |
69 | ||
70 | void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); | |
71 | ||
72 | /* Unwind descriptors. */ | |
73 | ||
74 | typedef struct | |
75 | { | |
76 | _uw16 length; | |
77 | _uw16 offset; | |
78 | } EHT16; | |
79 | ||
80 | typedef struct | |
81 | { | |
82 | _uw length; | |
83 | _uw offset; | |
84 | } EHT32; | |
85 | ||
86 | /* Calculate the address encoded by a 31-bit self-relative offset at address | |
87 | P. Copy of routine in unwind-arm.c. */ | |
88 | ||
89 | static inline _uw | |
90 | selfrel_offset31 (const _uw *p) | |
91 | { | |
92 | _uw offset; | |
93 | ||
94 | offset = *p; | |
95 | /* Sign extend to 32 bits. */ | |
96 | if (offset & (1 << 30)) | |
97 | offset |= 1u << 31; | |
98 | ||
99 | return offset + (_uw) p; | |
100 | } | |
101 | ||
102 | ||
103 | /* Personality routine helper functions. */ | |
104 | ||
105 | #define CODE_FINISH (0xe7) | |
106 | ||
107 | /* Return the next byte of unwinding information, or CODE_FINISH if there is | |
108 | no data remaining. */ | |
109 | static inline _uw8 | |
110 | next_unwind_byte (__gnu_unwind_state * uws) | |
111 | { | |
112 | _uw8 b; | |
113 | ||
114 | if (uws->bytes_left == 0) | |
115 | { | |
116 | /* Load another word */ | |
117 | if (uws->words_left == 0) | |
118 | return CODE_FINISH; /* Nothing left. */ | |
119 | uws->words_left--; | |
120 | uws->data = *(uws->next++); | |
121 | uws->bytes_left = 3; | |
122 | } | |
123 | else | |
124 | uws->bytes_left--; | |
125 | ||
126 | /* Extract the most significant byte. */ | |
127 | b = (uws->data >> 24) & 0xff; | |
128 | uws->data <<= 8; | |
129 | return b; | |
130 | } | |
131 | ||
132 | static void | |
133 | unwind_restore_pair (_Unwind_Context * context, int reg, _uw *ptr) | |
134 | { | |
135 | #ifdef _BIG_ENDIAN | |
136 | _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, ptr + 1); | |
137 | _Unwind_VRS_Set (context, _UVRSC_CORE, reg + 1, _UVRSD_UINT32, ptr); | |
138 | #else | |
139 | _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, ptr); | |
140 | _Unwind_VRS_Set (context, _UVRSC_CORE, reg + 1, _UVRSD_UINT32, ptr + 1); | |
141 | #endif | |
142 | } | |
143 | ||
144 | static const int | |
145 | unwind_frame_regs[13] = | |
146 | { | |
147 | R_A15, R_B15, R_B14, R_B13, R_B12, R_B11, R_B10, R_B3, | |
148 | R_A14, R_A13, R_A12, R_A11, R_A10 | |
149 | }; | |
150 | ||
151 | static void | |
152 | pop_compact_frame (_Unwind_Context * context, _uw mask, _uw *ptr, int inc_sp) | |
153 | { | |
154 | int size; | |
155 | _uw test; | |
0d53e346 | 156 | int i, regno, nregs; |
1e874273 PB |
157 | |
158 | size = 0; | |
159 | nregs = __builtin_popcount (mask); | |
160 | for (i = 0; i < 13; i++) | |
161 | { | |
162 | test = 1 << i; | |
163 | if ((mask & test) == 0) | |
164 | continue; | |
165 | ||
166 | regno = unwind_frame_regs[12 - i]; | |
1e874273 | 167 | |
0d53e346 BS |
168 | if (i < 12 && nregs > 2 |
169 | && (mask & (test << 1)) != 0 | |
170 | && unwind_frame_regs[11 - i] == regno + 1 | |
171 | && (regno & 1) == 0) | |
1e874273 PB |
172 | { |
173 | i++; | |
174 | nregs--; | |
175 | } | |
176 | ||
177 | nregs--; | |
178 | size += 2; | |
179 | } | |
180 | ||
181 | if (!inc_sp) | |
182 | ptr -= size; | |
183 | ||
184 | /* SP points just past the end of the stack. */ | |
185 | ptr += 2; | |
186 | nregs = __builtin_popcount (mask); | |
187 | for (i = 0; i < 13; i++) | |
188 | { | |
189 | test = 1 << i; | |
190 | if ((mask & test) == 0) | |
191 | continue; | |
192 | ||
193 | regno = unwind_frame_regs[12 - i]; | |
1e874273 | 194 | |
0d53e346 BS |
195 | if (i < 12 && nregs > 2 |
196 | && (mask & (test << 1)) != 0 | |
197 | && unwind_frame_regs[11 - i] == regno + 1 | |
198 | && (regno & 1) == 0) | |
1e874273 PB |
199 | { |
200 | /* Register pair. */ | |
201 | unwind_restore_pair (context, regno, ptr); | |
202 | i++; | |
203 | nregs--; | |
204 | } | |
205 | else | |
206 | { | |
207 | /* Single register with padding. */ | |
208 | _Unwind_VRS_Set (context, _UVRSC_CORE, regno, _UVRSD_UINT32, ptr); | |
209 | } | |
210 | ||
211 | nregs--; | |
212 | ptr += 2; | |
213 | } | |
214 | ||
215 | ptr -= 2; | |
216 | if ((mask & (1 << 11)) == 0) | |
217 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); | |
218 | } | |
219 | ||
220 | static void | |
221 | pop_frame (_Unwind_Context * context, _uw mask, _uw *ptr, int inc_sp) | |
222 | { | |
223 | int i; | |
224 | int regno; | |
225 | int nregs; | |
226 | ||
227 | nregs = __builtin_popcount (mask); | |
228 | ||
229 | if (!inc_sp) | |
230 | ptr -= nregs; | |
231 | else if (nregs & 1) | |
232 | ptr++; | |
233 | ||
234 | ptr++; | |
235 | for (i = 0; i < 13; i++) | |
236 | { | |
237 | if ((mask & (1 << i)) == 0) | |
238 | continue; | |
239 | regno = unwind_frame_regs[12 - i]; | |
0d53e346 | 240 | if (i < 12 && unwind_frame_regs[11 - i] == (regno + 1) |
1e874273 PB |
241 | && (mask & (1 << (i + 1))) != 0 |
242 | && (((_uw)ptr) & 4) == 0 | |
243 | && (regno & 1) == 0) | |
244 | { | |
245 | unwind_restore_pair (context, regno, ptr); | |
246 | i++; | |
247 | ptr += 2; | |
248 | } | |
249 | else | |
250 | { | |
251 | _Unwind_VRS_Set (context, _UVRSC_CORE, regno, _UVRSD_UINT32, | |
252 | ptr); | |
253 | ptr++; | |
254 | } | |
255 | } | |
256 | ||
257 | ptr--; | |
258 | if ((mask & (1 << 11)) == 0) | |
259 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); | |
260 | } | |
261 | ||
262 | /* Unwind a 24-bit encoded frame. */ | |
263 | _Unwind_Reason_Code | |
264 | __gnu_unwind_24bit (_Unwind_Context * context, _uw data, int compact) | |
265 | { | |
266 | _uw offset; | |
267 | _uw mask; | |
268 | _uw *ptr; | |
269 | _uw tmp; | |
5b2d9d90 BS |
270 | int ret_reg = unwind_frame_regs[data & 0xf]; |
271 | ||
272 | if (ret_reg != R_B3) | |
273 | { | |
274 | _Unwind_VRS_Get (context, _UVRSC_CORE, unwind_frame_regs[data & 0xf], | |
275 | _UVRSD_UINT32, &tmp); | |
276 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, &tmp); | |
277 | } | |
1e874273 PB |
278 | |
279 | mask = (data >> 4) & 0x1fff; | |
280 | ||
281 | offset = (data >> 17) & 0x7f; | |
282 | if (offset == 0x7f) | |
283 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_A15, _UVRSD_UINT32, &ptr); | |
284 | else | |
285 | { | |
286 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); | |
287 | ptr += offset * 2; | |
288 | } | |
289 | ||
290 | ||
291 | if (compact) | |
292 | pop_compact_frame (context, mask, ptr, offset != 0x7f); | |
293 | else | |
294 | pop_frame (context, mask, ptr, offset != 0x7f); | |
295 | ||
5b2d9d90 | 296 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, &tmp); |
1e874273 PB |
297 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, &tmp); |
298 | ||
299 | return _URC_OK; | |
300 | } | |
301 | ||
302 | static void | |
303 | unwind_pop_rts (_Unwind_Context * context) | |
304 | { | |
305 | _uw *ptr; | |
306 | ||
307 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); | |
308 | #ifdef _BIG_ENDIAN | |
309 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ptr + 1); | |
310 | #else | |
311 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ptr + 2); | |
312 | #endif | |
313 | ptr += 3; | |
314 | unwind_restore_pair (context, R_A10, ptr); | |
315 | ptr += 2; | |
316 | unwind_restore_pair (context, R_B10, ptr); | |
317 | ptr += 2; | |
318 | unwind_restore_pair (context, R_A12, ptr); | |
319 | ptr += 2; | |
320 | unwind_restore_pair (context, R_B12, ptr); | |
321 | ptr += 2; | |
322 | unwind_restore_pair (context, R_A14, ptr); | |
323 | ptr += 2; | |
324 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_B14, _UVRSD_UINT32, ptr); | |
325 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); | |
326 | /* PC will be set by implicit RETURN opcode. */ | |
327 | } | |
328 | ||
329 | /* Execute the unwinding instructions described by UWS. */ | |
330 | _Unwind_Reason_Code | |
331 | __gnu_unwind_execute (_Unwind_Context * context, __gnu_unwind_state * uws) | |
332 | { | |
333 | _uw op; | |
334 | int inc_sp; | |
335 | _uw reg; | |
336 | _uw *ptr; | |
337 | ||
338 | inc_sp = 1; | |
339 | for (;;) | |
340 | { | |
341 | op = next_unwind_byte (uws); | |
342 | if (op == CODE_FINISH) | |
343 | { | |
344 | /* Drop out of the loop. */ | |
345 | break; | |
346 | } | |
347 | if ((op & 0xc0) == 0) | |
348 | { | |
349 | /* sp += (imm6 << 3) + 8. */ | |
350 | _uw offset; | |
351 | ||
352 | offset = ((op & 0x3f) << 3) + 8; | |
353 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
354 | reg += offset; | |
355 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
356 | continue; | |
357 | } | |
358 | ||
359 | if (op == 0xd2) | |
360 | { | |
361 | /* vsp = vsp + 0x204 + (uleb128 << 2). */ | |
362 | int shift; | |
363 | ||
364 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
365 | op = next_unwind_byte (uws); | |
366 | shift = 3; | |
367 | while (op & 0x80) | |
368 | { | |
369 | reg += ((op & 0x7f) << shift); | |
370 | shift += 7; | |
371 | op = next_unwind_byte (uws); | |
372 | } | |
373 | reg += ((op & 0x7f) << shift) + 0x408; | |
374 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
375 | continue; | |
376 | } | |
377 | ||
378 | if ((op & 0xe0) == 0x80) | |
379 | { | |
380 | /* POP bitmask */ | |
381 | _uw mask = ((op & 0x1f) << 8) | next_unwind_byte (uws); | |
382 | ||
383 | if (mask == 0) | |
384 | { | |
385 | /* CANTUNWIND */ | |
386 | return _URC_FAILURE; | |
387 | } | |
388 | ||
389 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); | |
390 | pop_frame (context, mask, ptr, inc_sp); | |
391 | continue; | |
392 | } | |
393 | ||
394 | if ((op & 0xe0) == 0xa0) | |
395 | { | |
396 | /* POP bitmask (compact) */ | |
397 | _uw mask = ((op & 0x1f) << 8) | next_unwind_byte (uws); | |
398 | ||
399 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); | |
400 | pop_compact_frame (context, mask, ptr, inc_sp); | |
401 | continue; | |
402 | } | |
403 | ||
404 | if ((op & 0xf0) == 0xc0) | |
405 | { | |
406 | /* POP registers */ | |
407 | int nregs = op & 0xf; | |
408 | ||
409 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); | |
410 | while (nregs > 0) | |
411 | { | |
412 | op = next_unwind_byte (uws); | |
413 | if ((op >> 4) != 0xf) | |
414 | { | |
415 | reg = unwind_frame_regs[op >> 4]; | |
416 | _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, | |
417 | ptr); | |
418 | nregs--; | |
419 | } | |
420 | ptr--; | |
421 | if ((op & 0xf) != 0xf) | |
422 | { | |
423 | reg = unwind_frame_regs[op & 0xf]; | |
424 | _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, | |
425 | ptr); | |
426 | nregs--; | |
427 | } | |
428 | ptr--; | |
429 | } | |
430 | ||
431 | continue; | |
432 | } | |
433 | ||
434 | if (op == 0xd0) | |
435 | { | |
436 | /* MV FP, SP */ | |
437 | inc_sp = 0; | |
438 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_A15, _UVRSD_UINT32, ®); | |
439 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
440 | continue; | |
441 | } | |
442 | ||
443 | if (op == 0xd1) | |
444 | { | |
445 | /* __cx6abi_pop_rts */ | |
446 | unwind_pop_rts (context); | |
447 | break; | |
448 | } | |
449 | ||
450 | if ((op & 0xf0) == 0xe0) | |
451 | { | |
5764ee3c | 452 | /* B3 = reg. RETURN case already handled above. */ |
1e874273 PB |
453 | int regno = unwind_frame_regs[op & 0xf]; |
454 | ||
455 | _Unwind_VRS_Get (context, _UVRSC_CORE, regno, _UVRSD_UINT32, ®); | |
456 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ®); | |
457 | continue; | |
458 | } | |
459 | ||
460 | /* Reserved. */ | |
461 | return _URC_FAILURE; | |
462 | } | |
463 | ||
464 | /* Implicit RETURN. */ | |
465 | _Unwind_VRS_Get (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ®); | |
466 | _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, ®); | |
467 | return _URC_OK; | |
468 | } | |
469 | ||
470 | ||
471 | /* Execute the unwinding instructions associated with a frame. UCBP and | |
472 | CONTEXT are the current exception object and virtual CPU state | |
473 | respectively. */ | |
474 | ||
475 | _Unwind_Reason_Code | |
476 | __gnu_unwind_frame (_Unwind_Control_Block * ucbp, _Unwind_Context * context) | |
477 | { | |
478 | _uw *ptr; | |
479 | __gnu_unwind_state uws; | |
480 | ||
481 | ptr = (_uw *) ucbp->pr_cache.ehtp; | |
482 | /* Skip over the personality routine address. */ | |
483 | ptr++; | |
484 | /* Setup the unwinder state. */ | |
485 | uws.data = (*ptr) << 8; | |
486 | uws.next = ptr + 1; | |
487 | uws.bytes_left = 3; | |
488 | uws.words_left = ((*ptr) >> 24) & 0xff; | |
489 | ||
490 | return __gnu_unwind_execute (context, &uws); | |
491 | } | |
492 | ||
493 | /* Data segment base pointer corresponding to the function catching | |
494 | the exception. */ | |
495 | ||
496 | _Unwind_Ptr | |
497 | _Unwind_GetDataRelBase (_Unwind_Context *context) | |
498 | { | |
499 | return _Unwind_GetGR (context, R_B14); | |
500 | } | |
501 | ||
502 | /* This should never be used. */ | |
503 | ||
504 | _Unwind_Ptr | |
505 | _Unwind_GetTextRelBase (_Unwind_Context *context __attribute__ ((unused))) | |
506 | { | |
507 | abort (); | |
508 | } | |
509 | ||
510 | /* Only used by gcc personality routines, so can rely on a value they hid | |
511 | there earlier. */ | |
512 | _Unwind_Ptr | |
513 | _Unwind_GetRegionStart (_Unwind_Context *context) | |
514 | { | |
515 | _Unwind_Control_Block *ucbp; | |
516 | ||
517 | ucbp = (_Unwind_Control_Block *) _Unwind_GetGR (context, UNWIND_POINTER_REG); | |
518 | return (_Unwind_Ptr) ucbp->pr_cache.fnstart; | |
519 | } | |
520 | ||
521 | void * | |
522 | _Unwind_GetLanguageSpecificData (_Unwind_Context *context) | |
523 | { | |
524 | _Unwind_Control_Block *ucbp; | |
525 | _uw *ptr; | |
526 | ||
527 | ucbp = (_Unwind_Control_Block *) _Unwind_GetGR (context, UNWIND_POINTER_REG); | |
528 | ptr = (_uw *) ucbp->pr_cache.ehtp; | |
529 | /* Skip the personality routine address. */ | |
530 | ptr++; | |
531 | /* Skip the unwind opcodes. */ | |
532 | ptr += (((*ptr) >> 24) & 0xff) + 1; | |
533 | ||
534 | return ptr; | |
535 | } |