| /* C6X ABI compliant unwinding routines |
| Copyright (C) 2011-2021 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; |
| } |