Viewing file: pr-support.c (11.98 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/* C6X ABI compliant unwinding routines Copyright (C) 2011-2022 Free Software Foundation, Inc. This file is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.
This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
Under Section 7 of GPL version 3, you are granted additional permissions described in the GCC Runtime Library Exception, version 3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and a copy of the GCC Runtime Library Exception along with this program; see the files COPYING3 and COPYING.RUNTIME respectively. If not, see <http://www.gnu.org/licenses/>. */
#include "unwind.h"
/* We add a prototype for abort here to avoid creating a dependency on target headers. */ extern void abort (void);
typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
/* Misc constants. */ #define R_A0 0 #define R_A1 1 #define R_A2 2 #define R_A3 3 #define R_A4 4 #define R_A5 5 #define R_A6 6 #define R_A7 7 #define R_A8 8 #define R_A9 9 #define R_A10 10 #define R_A11 11 #define R_A12 12 #define R_A13 13 #define R_A14 14 #define R_A15 15 #define R_B0 16 #define R_B1 17 #define R_B2 18 #define R_B3 19 #define R_B4 20 #define R_B5 21 #define R_B6 22 #define R_B7 23 #define R_B8 24 #define R_B9 25 #define R_B10 26 #define R_B11 27 #define R_B12 28 #define R_B13 29 #define R_B14 30 #define R_B15 31
#define R_SP R_B15 #define R_PC 33
#define uint32_highbit (((_uw) 1) << 31)
void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
/* Unwind descriptors. */
typedef struct { _uw16 length; _uw16 offset; } EHT16;
typedef struct { _uw length; _uw offset; } EHT32;
/* Calculate the address encoded by a 31-bit self-relative offset at address P. Copy of routine in unwind-arm.c. */
static inline _uw selfrel_offset31 (const _uw *p) { _uw offset;
offset = *p; /* Sign extend to 32 bits. */ if (offset & (1 << 30)) offset |= 1u << 31;
return offset + (_uw) p; }
/* Personality routine helper functions. */
#define CODE_FINISH (0xe7)
/* Return the next byte of unwinding information, or CODE_FINISH if there is no data remaining. */ static inline _uw8 next_unwind_byte (__gnu_unwind_state * uws) { _uw8 b;
if (uws->bytes_left == 0) { /* Load another word */ if (uws->words_left == 0) return CODE_FINISH; /* Nothing left. */ uws->words_left--; uws->data = *(uws->next++); uws->bytes_left = 3; } else uws->bytes_left--;
/* Extract the most significant byte. */ b = (uws->data >> 24) & 0xff; uws->data <<= 8; return b; }
static void unwind_restore_pair (_Unwind_Context * context, int reg, _uw *ptr) { #ifdef _BIG_ENDIAN _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, ptr + 1); _Unwind_VRS_Set (context, _UVRSC_CORE, reg + 1, _UVRSD_UINT32, ptr); #else _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, ptr); _Unwind_VRS_Set (context, _UVRSC_CORE, reg + 1, _UVRSD_UINT32, ptr + 1); #endif }
static const int unwind_frame_regs[13] = { R_A15, R_B15, R_B14, R_B13, R_B12, R_B11, R_B10, R_B3, R_A14, R_A13, R_A12, R_A11, R_A10 };
static void pop_compact_frame (_Unwind_Context * context, _uw mask, _uw *ptr, int inc_sp) { int size; _uw test; int i, regno, nregs;
size = 0; nregs = __builtin_popcount (mask); for (i = 0; i < 13; i++) { test = 1 << i; if ((mask & test) == 0) continue;
regno = unwind_frame_regs[12 - i];
if (i < 12 && nregs > 2 && (mask & (test << 1)) != 0 && unwind_frame_regs[11 - i] == regno + 1 && (regno & 1) == 0) { i++; nregs--; }
nregs--; size += 2; }
if (!inc_sp) ptr -= size;
/* SP points just past the end of the stack. */ ptr += 2; nregs = __builtin_popcount (mask); for (i = 0; i < 13; i++) { test = 1 << i; if ((mask & test) == 0) continue;
regno = unwind_frame_regs[12 - i];
if (i < 12 && nregs > 2 && (mask & (test << 1)) != 0 && unwind_frame_regs[11 - i] == regno + 1 && (regno & 1) == 0) { /* Register pair. */ unwind_restore_pair (context, regno, ptr); i++; nregs--; } else { /* Single register with padding. */ _Unwind_VRS_Set (context, _UVRSC_CORE, regno, _UVRSD_UINT32, ptr); }
nregs--; ptr += 2; }
ptr -= 2; if ((mask & (1 << 11)) == 0) _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); }
static void pop_frame (_Unwind_Context * context, _uw mask, _uw *ptr, int inc_sp) { int i; int regno; int nregs;
nregs = __builtin_popcount (mask);
if (!inc_sp) ptr -= nregs; else if (nregs & 1) ptr++;
ptr++; for (i = 0; i < 13; i++) { if ((mask & (1 << i)) == 0) continue; regno = unwind_frame_regs[12 - i]; if (i < 12 && unwind_frame_regs[11 - i] == (regno + 1) && (mask & (1 << (i + 1))) != 0 && (((_uw)ptr) & 4) == 0 && (regno & 1) == 0) { unwind_restore_pair (context, regno, ptr); i++; ptr += 2; } else { _Unwind_VRS_Set (context, _UVRSC_CORE, regno, _UVRSD_UINT32, ptr); ptr++; } }
ptr--; if ((mask & (1 << 11)) == 0) _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); }
/* Unwind a 24-bit encoded frame. */ _Unwind_Reason_Code __gnu_unwind_24bit (_Unwind_Context * context, _uw data, int compact) { _uw offset; _uw mask; _uw *ptr; _uw tmp; int ret_reg = unwind_frame_regs[data & 0xf];
if (ret_reg != R_B3) { _Unwind_VRS_Get (context, _UVRSC_CORE, unwind_frame_regs[data & 0xf], _UVRSD_UINT32, &tmp); _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, &tmp); }
mask = (data >> 4) & 0x1fff;
offset = (data >> 17) & 0x7f; if (offset == 0x7f) _Unwind_VRS_Get (context, _UVRSC_CORE, R_A15, _UVRSD_UINT32, &ptr); else { _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); ptr += offset * 2; }
if (compact) pop_compact_frame (context, mask, ptr, offset != 0x7f); else pop_frame (context, mask, ptr, offset != 0x7f);
_Unwind_VRS_Get (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, &tmp); _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, &tmp);
return _URC_OK; }
static void unwind_pop_rts (_Unwind_Context * context) { _uw *ptr;
_Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); #ifdef _BIG_ENDIAN _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ptr + 1); #else _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ptr + 2); #endif ptr += 3; unwind_restore_pair (context, R_A10, ptr); ptr += 2; unwind_restore_pair (context, R_B10, ptr); ptr += 2; unwind_restore_pair (context, R_A12, ptr); ptr += 2; unwind_restore_pair (context, R_B12, ptr); ptr += 2; unwind_restore_pair (context, R_A14, ptr); ptr += 2; _Unwind_VRS_Set (context, _UVRSC_CORE, R_B14, _UVRSD_UINT32, ptr); _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); /* PC will be set by implicit RETURN opcode. */ }
/* Execute the unwinding instructions described by UWS. */ _Unwind_Reason_Code __gnu_unwind_execute (_Unwind_Context * context, __gnu_unwind_state * uws) { _uw op; int inc_sp; _uw reg; _uw *ptr;
inc_sp = 1; for (;;) { op = next_unwind_byte (uws); if (op == CODE_FINISH) { /* Drop out of the loop. */ break; } if ((op & 0xc0) == 0) { /* sp += (imm6 << 3) + 8. */ _uw offset;
offset = ((op & 0x3f) << 3) + 8; _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); reg += offset; _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); continue; }
if (op == 0xd2) { /* vsp = vsp + 0x204 + (uleb128 << 2). */ int shift;
_Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); op = next_unwind_byte (uws); shift = 3; while (op & 0x80) { reg += ((op & 0x7f) << shift); shift += 7; op = next_unwind_byte (uws); } reg += ((op & 0x7f) << shift) + 0x408; _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); continue; }
if ((op & 0xe0) == 0x80) { /* POP bitmask */ _uw mask = ((op & 0x1f) << 8) | next_unwind_byte (uws);
if (mask == 0) { /* CANTUNWIND */ return _URC_FAILURE; }
_Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); pop_frame (context, mask, ptr, inc_sp); continue; }
if ((op & 0xe0) == 0xa0) { /* POP bitmask (compact) */ _uw mask = ((op & 0x1f) << 8) | next_unwind_byte (uws);
_Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); pop_compact_frame (context, mask, ptr, inc_sp); continue; }
if ((op & 0xf0) == 0xc0) { /* POP registers */ int nregs = op & 0xf;
_Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); while (nregs > 0) { op = next_unwind_byte (uws); if ((op >> 4) != 0xf) { reg = unwind_frame_regs[op >> 4]; _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, ptr); nregs--; } ptr--; if ((op & 0xf) != 0xf) { reg = unwind_frame_regs[op & 0xf]; _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, ptr); nregs--; } ptr--; }
continue; }
if (op == 0xd0) { /* MV FP, SP */ inc_sp = 0; _Unwind_VRS_Get (context, _UVRSC_CORE, R_A15, _UVRSD_UINT32, ®); _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); continue; }
if (op == 0xd1) { /* __cx6abi_pop_rts */ unwind_pop_rts (context); break; }
if ((op & 0xf0) == 0xe0) { /* B3 = reg. RETURN case already handled above. */ int regno = unwind_frame_regs[op & 0xf];
_Unwind_VRS_Get (context, _UVRSC_CORE, regno, _UVRSD_UINT32, ®); _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ®); continue; } /* Reserved. */ return _URC_FAILURE; }
/* Implicit RETURN. */ _Unwind_VRS_Get (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ®); _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, ®); return _URC_OK; }
/* Execute the unwinding instructions associated with a frame. UCBP and CONTEXT are the current exception object and virtual CPU state respectively. */
_Unwind_Reason_Code __gnu_unwind_frame (_Unwind_Control_Block * ucbp, _Unwind_Context * context) { _uw *ptr; __gnu_unwind_state uws;
ptr = (_uw *) ucbp->pr_cache.ehtp; /* Skip over the personality routine address. */ ptr++; /* Setup the unwinder state. */ uws.data = (*ptr) << 8; uws.next = ptr + 1; uws.bytes_left = 3; uws.words_left = ((*ptr) >> 24) & 0xff;
return __gnu_unwind_execute (context, &uws); }
/* Data segment base pointer corresponding to the function catching the exception. */
_Unwind_Ptr _Unwind_GetDataRelBase (_Unwind_Context *context) { return _Unwind_GetGR (context, R_B14); }
/* This should never be used. */
_Unwind_Ptr _Unwind_GetTextRelBase (_Unwind_Context *context __attribute__ ((unused))) { abort (); }
/* Only used by gcc personality routines, so can rely on a value they hid there earlier. */ _Unwind_Ptr _Unwind_GetRegionStart (_Unwind_Context *context) { _Unwind_Control_Block *ucbp; ucbp = (_Unwind_Control_Block *) _Unwind_GetGR (context, UNWIND_POINTER_REG); return (_Unwind_Ptr) ucbp->pr_cache.fnstart; }
void * _Unwind_GetLanguageSpecificData (_Unwind_Context *context) { _Unwind_Control_Block *ucbp; _uw *ptr; ucbp = (_Unwind_Control_Block *) _Unwind_GetGR (context, UNWIND_POINTER_REG); ptr = (_uw *) ucbp->pr_cache.ehtp; /* Skip the personality routine address. */ ptr++; /* Skip the unwind opcodes. */ ptr += (((*ptr) >> 24) & 0xff) + 1;
return ptr; }
|