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