|  | /* DWARF2 exception handling and frame unwinding for Xtensa. | 
|  | Copyright (C) 1997-2021 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GCC. | 
|  |  | 
|  | GCC 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. | 
|  |  | 
|  | GCC 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 "tconfig.h" | 
|  | #include "tsystem.h" | 
|  | #include "coretypes.h" | 
|  | #include "tm.h" | 
|  | #include "libgcc_tm.h" | 
|  | #include "dwarf2.h" | 
|  | #include "unwind.h" | 
|  | #ifdef __USING_SJLJ_EXCEPTIONS__ | 
|  | # define NO_SIZE_OF_ENCODED_VALUE | 
|  | #endif | 
|  | #include "unwind-pe.h" | 
|  | #include "unwind-dw2-fde.h" | 
|  | #include "unwind-dw2-xtensa.h" | 
|  |  | 
|  | #ifndef __USING_SJLJ_EXCEPTIONS__ | 
|  |  | 
|  | /* The standard CIE and FDE structures work fine for Xtensa but the | 
|  | variable-size register window save areas are not a good fit for the rest | 
|  | of the standard DWARF unwinding mechanism.  Nor is that mechanism | 
|  | necessary, since the register save areas are always in fixed locations | 
|  | in each stack frame.  This file is a stripped down and customized version | 
|  | of the standard DWARF unwinding code.  It needs to be customized to have | 
|  | builtin logic for finding the save areas and also to track the stack | 
|  | pointer value (besides the CFA) while unwinding since the primary save | 
|  | area is located below the stack pointer.  It is stripped down to reduce | 
|  | code size and ease the maintenance burden of tracking changes in the | 
|  | standard version of the code.  */ | 
|  |  | 
|  | #ifndef DWARF_REG_TO_UNWIND_COLUMN | 
|  | #define DWARF_REG_TO_UNWIND_COLUMN(REGNO) (REGNO) | 
|  | #endif | 
|  |  | 
|  | #define XTENSA_RA_FIELD_MASK 0x3FFFFFFF | 
|  |  | 
|  | /* This is the register and unwind state for a particular frame.  This | 
|  | provides the information necessary to unwind up past a frame and return | 
|  | to its caller.  */ | 
|  | struct _Unwind_Context | 
|  | { | 
|  | /* Track register window save areas of 4 registers each, instead of | 
|  | keeping separate addresses for the individual registers.  */ | 
|  | _Unwind_Word *reg[4]; | 
|  |  | 
|  | void *cfa; | 
|  | void *sp; | 
|  | void *ra; | 
|  |  | 
|  | /* Cache the 2 high bits to replace the window size in return addresses.  */ | 
|  | _Unwind_Word ra_high_bits; | 
|  |  | 
|  | void *lsda; | 
|  | struct dwarf_eh_bases bases; | 
|  | /* Signal frame context.  */ | 
|  | #define SIGNAL_FRAME_BIT ((~(_Unwind_Word) 0 >> 1) + 1) | 
|  | _Unwind_Word flags; | 
|  | /* 0 for now, can be increased when further fields are added to | 
|  | struct _Unwind_Context.  */ | 
|  | _Unwind_Word version; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* Read unaligned data from the instruction buffer.  */ | 
|  |  | 
|  | union unaligned | 
|  | { | 
|  | void *p; | 
|  | } __attribute__ ((packed)); | 
|  |  | 
|  | static void uw_update_context (struct _Unwind_Context *, _Unwind_FrameState *); | 
|  | static _Unwind_Reason_Code uw_frame_state_for (struct _Unwind_Context *, | 
|  | _Unwind_FrameState *); | 
|  |  | 
|  | static inline void * | 
|  | read_pointer (const void *p) { const union unaligned *up = p; return up->p; } | 
|  |  | 
|  | static inline _Unwind_Word | 
|  | _Unwind_IsSignalFrame (struct _Unwind_Context *context) | 
|  | { | 
|  | return (context->flags & SIGNAL_FRAME_BIT) ? 1 : 0; | 
|  | } | 
|  |  | 
|  | static inline void | 
|  | _Unwind_SetSignalFrame (struct _Unwind_Context *context, int val) | 
|  | { | 
|  | if (val) | 
|  | context->flags |= SIGNAL_FRAME_BIT; | 
|  | else | 
|  | context->flags &= ~SIGNAL_FRAME_BIT; | 
|  | } | 
|  |  | 
|  | /* Get the value of register INDEX as saved in CONTEXT.  */ | 
|  |  | 
|  | inline _Unwind_Word | 
|  | _Unwind_GetGR (struct _Unwind_Context *context, int index) | 
|  | { | 
|  | _Unwind_Word *ptr; | 
|  |  | 
|  | index = DWARF_REG_TO_UNWIND_COLUMN (index); | 
|  | ptr = context->reg[index >> 2] + (index & 3); | 
|  |  | 
|  | return *ptr; | 
|  | } | 
|  |  | 
|  | /* Get the value of the CFA as saved in CONTEXT.  */ | 
|  |  | 
|  | _Unwind_Word | 
|  | _Unwind_GetCFA (struct _Unwind_Context *context) | 
|  | { | 
|  | return (_Unwind_Ptr) context->sp; | 
|  | } | 
|  |  | 
|  | /* Overwrite the saved value for register INDEX in CONTEXT with VAL.  */ | 
|  |  | 
|  | inline void | 
|  | _Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val) | 
|  | { | 
|  | _Unwind_Word *ptr; | 
|  |  | 
|  | index = DWARF_REG_TO_UNWIND_COLUMN (index); | 
|  | ptr = context->reg[index >> 2] + (index & 3); | 
|  |  | 
|  | *ptr = val; | 
|  | } | 
|  |  | 
|  | /* Retrieve the return address for CONTEXT.  */ | 
|  |  | 
|  | inline _Unwind_Ptr | 
|  | _Unwind_GetIP (struct _Unwind_Context *context) | 
|  | { | 
|  | return (_Unwind_Ptr) context->ra; | 
|  | } | 
|  |  | 
|  | /* Retrieve the return address and flag whether that IP is before | 
|  | or after first not yet fully executed instruction.  */ | 
|  |  | 
|  | inline _Unwind_Ptr | 
|  | _Unwind_GetIPInfo (struct _Unwind_Context *context, int *ip_before_insn) | 
|  | { | 
|  | *ip_before_insn = _Unwind_IsSignalFrame (context); | 
|  | return (_Unwind_Ptr) context->ra; | 
|  | } | 
|  |  | 
|  | /* Overwrite the return address for CONTEXT with VAL.  */ | 
|  |  | 
|  | inline void | 
|  | _Unwind_SetIP (struct _Unwind_Context *context, _Unwind_Ptr val) | 
|  | { | 
|  | context->ra = (void *) val; | 
|  | } | 
|  |  | 
|  | void * | 
|  | _Unwind_GetLanguageSpecificData (struct _Unwind_Context *context) | 
|  | { | 
|  | return context->lsda; | 
|  | } | 
|  |  | 
|  | _Unwind_Ptr | 
|  | _Unwind_GetRegionStart (struct _Unwind_Context *context) | 
|  | { | 
|  | return (_Unwind_Ptr) context->bases.func; | 
|  | } | 
|  |  | 
|  | void * | 
|  | _Unwind_FindEnclosingFunction (void *pc) | 
|  | { | 
|  | struct dwarf_eh_bases bases; | 
|  | const struct dwarf_fde *fde = _Unwind_Find_FDE (pc-1, &bases); | 
|  | if (fde) | 
|  | return bases.func; | 
|  | else | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | _Unwind_Ptr | 
|  | _Unwind_GetDataRelBase (struct _Unwind_Context *context) | 
|  | { | 
|  | return (_Unwind_Ptr) context->bases.dbase; | 
|  | } | 
|  |  | 
|  | _Unwind_Ptr | 
|  | _Unwind_GetTextRelBase (struct _Unwind_Context *context) | 
|  | { | 
|  | return (_Unwind_Ptr) context->bases.tbase; | 
|  | } | 
|  |  | 
|  | #include "md-unwind-support.h" | 
|  |  | 
|  | /* Extract any interesting information from the CIE for the translation | 
|  | unit F belongs to.  Return a pointer to the byte after the augmentation, | 
|  | or NULL if we encountered an undecipherable augmentation.  */ | 
|  |  | 
|  | static const unsigned char * | 
|  | extract_cie_info (const struct dwarf_cie *cie, struct _Unwind_Context *context, | 
|  | _Unwind_FrameState *fs) | 
|  | { | 
|  | const unsigned char *aug = cie->augmentation; | 
|  | const unsigned char *p = aug + strlen ((const char *)aug) + 1; | 
|  | const unsigned char *ret = NULL; | 
|  | _uleb128_t utmp; | 
|  | _sleb128_t stmp; | 
|  |  | 
|  | /* g++ v2 "eh" has pointer immediately following augmentation string, | 
|  | so it must be handled first.  */ | 
|  | if (aug[0] == 'e' && aug[1] == 'h') | 
|  | { | 
|  | fs->eh_ptr = read_pointer (p); | 
|  | p += sizeof (void *); | 
|  | aug += 2; | 
|  | } | 
|  |  | 
|  | /* Immediately following the augmentation are the code and | 
|  | data alignment and return address column.  */ | 
|  | p = read_uleb128 (p, &utmp); | 
|  | p = read_sleb128 (p, &stmp); | 
|  | if (cie->version == 1) | 
|  | fs->retaddr_column = *p++; | 
|  | else | 
|  | { | 
|  | p = read_uleb128 (p, &utmp); | 
|  | fs->retaddr_column = (_Unwind_Word)utmp; | 
|  | } | 
|  | fs->lsda_encoding = DW_EH_PE_omit; | 
|  |  | 
|  | /* If the augmentation starts with 'z', then a uleb128 immediately | 
|  | follows containing the length of the augmentation field following | 
|  | the size.  */ | 
|  | if (*aug == 'z') | 
|  | { | 
|  | p = read_uleb128 (p, &utmp); | 
|  | ret = p + utmp; | 
|  |  | 
|  | fs->saw_z = 1; | 
|  | ++aug; | 
|  | } | 
|  |  | 
|  | /* Iterate over recognized augmentation subsequences.  */ | 
|  | while (*aug != '\0') | 
|  | { | 
|  | /* "L" indicates a byte showing how the LSDA pointer is encoded.  */ | 
|  | if (aug[0] == 'L') | 
|  | { | 
|  | fs->lsda_encoding = *p++; | 
|  | aug += 1; | 
|  | } | 
|  |  | 
|  | /* "R" indicates a byte indicating how FDE addresses are encoded.  */ | 
|  | else if (aug[0] == 'R') | 
|  | { | 
|  | fs->fde_encoding = *p++; | 
|  | aug += 1; | 
|  | } | 
|  |  | 
|  | /* "P" indicates a personality routine in the CIE augmentation.  */ | 
|  | else if (aug[0] == 'P') | 
|  | { | 
|  | _Unwind_Ptr personality; | 
|  |  | 
|  | p = read_encoded_value (context, *p, p + 1, &personality); | 
|  | fs->personality = (_Unwind_Personality_Fn) personality; | 
|  | aug += 1; | 
|  | } | 
|  |  | 
|  | /* "S" indicates a signal frame.  */ | 
|  | else if (aug[0] == 'S') | 
|  | { | 
|  | fs->signal_frame = 1; | 
|  | aug += 1; | 
|  | } | 
|  |  | 
|  | /* Otherwise we have an unknown augmentation string. | 
|  | Bail unless we saw a 'z' prefix.  */ | 
|  | else | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return ret ? ret : p; | 
|  | } | 
|  |  | 
|  | /* Given the _Unwind_Context CONTEXT for a stack frame, look up the FDE for | 
|  | its caller and decode it into FS.  This function also sets the | 
|  | lsda member of CONTEXT, as it is really information | 
|  | about the caller's frame.  */ | 
|  |  | 
|  | static _Unwind_Reason_Code | 
|  | uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs) | 
|  | { | 
|  | const struct dwarf_fde *fde; | 
|  | const struct dwarf_cie *cie; | 
|  | const unsigned char *aug; | 
|  | int window_size; | 
|  | _Unwind_Word *ra_ptr; | 
|  |  | 
|  | memset (fs, 0, sizeof (*fs)); | 
|  | context->lsda = 0; | 
|  |  | 
|  | fde = _Unwind_Find_FDE (context->ra + _Unwind_IsSignalFrame (context) - 1, | 
|  | &context->bases); | 
|  | if (fde == NULL) | 
|  | { | 
|  | #ifdef MD_FALLBACK_FRAME_STATE_FOR | 
|  | _Unwind_Reason_Code reason; | 
|  | /* Couldn't find frame unwind info for this function.  Try a | 
|  | target-specific fallback mechanism.  This will necessarily | 
|  | not provide a personality routine or LSDA.  */ | 
|  | reason = MD_FALLBACK_FRAME_STATE_FOR (context, fs); | 
|  | if (reason != _URC_END_OF_STACK) | 
|  | return reason; | 
|  | #endif | 
|  | /* The frame was not recognized and handled by the fallback function, | 
|  | but it is not really the end of the stack.  Fall through here and | 
|  | unwind it anyway.  */ | 
|  | } | 
|  | else | 
|  | { | 
|  | cie = get_cie (fde); | 
|  | if (extract_cie_info (cie, context, fs) == NULL) | 
|  | /* CIE contained unknown augmentation.  */ | 
|  | return _URC_FATAL_PHASE1_ERROR; | 
|  |  | 
|  | /* Locate augmentation for the fde.  */ | 
|  | aug = (const unsigned char *) fde + sizeof (*fde); | 
|  | aug += 2 * size_of_encoded_value (fs->fde_encoding); | 
|  | if (fs->saw_z) | 
|  | { | 
|  | _uleb128_t i; | 
|  | aug = read_uleb128 (aug, &i); | 
|  | } | 
|  | if (fs->lsda_encoding != DW_EH_PE_omit) | 
|  | { | 
|  | _Unwind_Ptr lsda; | 
|  |  | 
|  | aug = read_encoded_value (context, fs->lsda_encoding, aug, &lsda); | 
|  | context->lsda = (void *) lsda; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Check for the end of the stack.  This needs to be checked after | 
|  | the MD_FALLBACK_FRAME_STATE_FOR check for signal frames because | 
|  | the contents of context->reg[0] are undefined at a signal frame, | 
|  | and register a0 may appear to be zero.  (The return address in | 
|  | context->ra comes from register a4 or a8).  */ | 
|  | ra_ptr = context->reg[0]; | 
|  | if (ra_ptr && *ra_ptr == 0) | 
|  | return _URC_END_OF_STACK; | 
|  |  | 
|  | /* Find the window size from the high bits of the return address.  */ | 
|  | if (ra_ptr) | 
|  | window_size = (*ra_ptr >> 30) * 4; | 
|  | else | 
|  | window_size = 8; | 
|  |  | 
|  | fs->retaddr_column = window_size; | 
|  |  | 
|  | return _URC_NO_REASON; | 
|  | } | 
|  |  | 
|  | static void | 
|  | uw_update_context_1 (struct _Unwind_Context *context, _Unwind_FrameState *fs) | 
|  | { | 
|  | struct _Unwind_Context orig_context = *context; | 
|  | _Unwind_Word *sp, *cfa, *next_cfa; | 
|  | int i; | 
|  |  | 
|  | if (fs->signal_regs) | 
|  | { | 
|  | cfa = (_Unwind_Word *) fs->signal_regs[1]; | 
|  | next_cfa = (_Unwind_Word *) cfa[-3]; | 
|  |  | 
|  | for (i = 0; i < 4; i++) | 
|  | context->reg[i] = fs->signal_regs + (i << 2); | 
|  | } | 
|  | else | 
|  | { | 
|  | int window_size = fs->retaddr_column >> 2; | 
|  |  | 
|  | sp = (_Unwind_Word *) orig_context.sp; | 
|  | cfa = (_Unwind_Word *) orig_context.cfa; | 
|  | next_cfa = (_Unwind_Word *) cfa[-3]; | 
|  |  | 
|  | /* Registers a0-a3 are in the save area below sp.  */ | 
|  | context->reg[0] = sp - 4; | 
|  |  | 
|  | /* Find the extra save area below next_cfa.  */ | 
|  | for (i = 1; i < window_size; i++) | 
|  | context->reg[i] = next_cfa - 4 * (1 + window_size - i); | 
|  |  | 
|  | /* Remaining registers rotate from previous save areas.  */ | 
|  | for (i = window_size; i < 4; i++) | 
|  | context->reg[i] = orig_context.reg[i - window_size]; | 
|  | } | 
|  |  | 
|  | context->sp = cfa; | 
|  | context->cfa = next_cfa; | 
|  |  | 
|  | _Unwind_SetSignalFrame (context, fs->signal_frame); | 
|  | } | 
|  |  | 
|  | /* CONTEXT describes the unwind state for a frame, and FS describes the FDE | 
|  | of its caller.  Update CONTEXT to refer to the caller as well.  Note | 
|  | that the lsda member is not updated here, but later in | 
|  | uw_frame_state_for.  */ | 
|  |  | 
|  | static void | 
|  | uw_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs) | 
|  | { | 
|  | uw_update_context_1 (context, fs); | 
|  |  | 
|  | /* Compute the return address now, since the return address column | 
|  | can change from frame to frame.  */ | 
|  | if (fs->signal_ra != 0) | 
|  | context->ra = (void *) fs->signal_ra; | 
|  | else | 
|  | context->ra = (void *) ((_Unwind_GetGR (context, fs->retaddr_column) | 
|  | & XTENSA_RA_FIELD_MASK) | context->ra_high_bits); | 
|  | } | 
|  |  | 
|  | static void | 
|  | uw_advance_context (struct _Unwind_Context *context, _Unwind_FrameState *fs) | 
|  | { | 
|  | uw_update_context (context, fs); | 
|  | } | 
|  |  | 
|  | /* Fill in CONTEXT for top-of-stack.  The only valid registers at this | 
|  | level will be the return address and the CFA.  */ | 
|  |  | 
|  | #define uw_init_context(CONTEXT)					   \ | 
|  | do									   \ | 
|  | {									   \ | 
|  | __builtin_unwind_init ();						   \ | 
|  | uw_init_context_1 (CONTEXT, __builtin_dwarf_cfa (),		   \ | 
|  | __builtin_return_address (0));			   \ | 
|  | }									   \ | 
|  | while (0) | 
|  |  | 
|  | static void __attribute__((noinline)) | 
|  | uw_init_context_1 (struct _Unwind_Context *context, void *outer_cfa, | 
|  | void *outer_ra) | 
|  | { | 
|  | void *ra = __builtin_return_address (0); | 
|  | void *cfa = __builtin_dwarf_cfa (); | 
|  | _Unwind_FrameState fs; | 
|  |  | 
|  | memset (context, 0, sizeof (struct _Unwind_Context)); | 
|  | context->ra = ra; | 
|  |  | 
|  | memset (&fs, 0, sizeof (fs)); | 
|  | fs.retaddr_column = 8; | 
|  | context->sp = cfa; | 
|  | context->cfa = outer_cfa; | 
|  | context->ra_high_bits = | 
|  | ((_Unwind_Word) uw_init_context_1) & ~XTENSA_RA_FIELD_MASK; | 
|  | uw_update_context_1 (context, &fs); | 
|  |  | 
|  | context->ra = outer_ra; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Install TARGET into CURRENT so that we can return to it.  This is a | 
|  | macro because __builtin_eh_return must be invoked in the context of | 
|  | our caller, and also because spilling registers of the caller before | 
|  | the context installation may result in reload of wrong register values | 
|  | after the context installation due to the change of the stack pointer | 
|  | in the base save area.  This spilling may be caused by an interrupt | 
|  | handler on baremetal host.  */ | 
|  |  | 
|  | #define uw_install_context(CURRENT, TARGET, FRAMES)			 \ | 
|  | do									 \ | 
|  | {									 \ | 
|  | void *handler = __builtin_frob_return_addr ((TARGET)->ra);	 \ | 
|  | long i;								 \ | 
|  | \ | 
|  | /* The eh_return insn assumes a window size of 8, so don't bother	 \ | 
|  | copying the save areas for registers a8-a15 since they won't be \ | 
|  | reloaded.  */							 \ | 
|  | for (i = 0; i < 2; ++i)						 \ | 
|  | {								 \ | 
|  | _Unwind_Word *c = (CURRENT)->reg[i];				 \ | 
|  | _Unwind_Word *t = (TARGET)->reg[i];				 \ | 
|  | int j;							 \ | 
|  | \ | 
|  | if (t && c && t != c)						 \ | 
|  | for (j = 0; j < 4; ++j)					 \ | 
|  | *c++ = *t++;						 \ | 
|  | }								 \ | 
|  | __builtin_eh_return (0, handler);					 \ | 
|  | }									 \ | 
|  | while (0) | 
|  |  | 
|  | static inline _Unwind_Ptr | 
|  | uw_identify_context (struct _Unwind_Context *context) | 
|  | { | 
|  | return _Unwind_GetCFA (context); | 
|  | } | 
|  |  | 
|  |  | 
|  | #include "unwind.inc" | 
|  |  | 
|  | #if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS) | 
|  | alias (_Unwind_Backtrace); | 
|  | alias (_Unwind_DeleteException); | 
|  | alias (_Unwind_FindEnclosingFunction); | 
|  | alias (_Unwind_ForcedUnwind); | 
|  | alias (_Unwind_GetDataRelBase); | 
|  | alias (_Unwind_GetTextRelBase); | 
|  | alias (_Unwind_GetCFA); | 
|  | alias (_Unwind_GetGR); | 
|  | alias (_Unwind_GetIP); | 
|  | alias (_Unwind_GetLanguageSpecificData); | 
|  | alias (_Unwind_GetRegionStart); | 
|  | alias (_Unwind_RaiseException); | 
|  | alias (_Unwind_Resume); | 
|  | alias (_Unwind_Resume_or_Rethrow); | 
|  | alias (_Unwind_SetGR); | 
|  | alias (_Unwind_SetIP); | 
|  | #endif | 
|  |  | 
|  | #endif /* !USING_SJLJ_EXCEPTIONS */ |