summaryrefslogtreecommitdiff
path: root/gcc/config/arm/unwind-arm.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/arm/unwind-arm.c')
-rw-r--r--gcc/config/arm/unwind-arm.c121
1 files changed, 93 insertions, 28 deletions
diff --git a/gcc/config/arm/unwind-arm.c b/gcc/config/arm/unwind-arm.c
index 4d703dbf09a..b3f8a00dcb5 100644
--- a/gcc/config/arm/unwind-arm.c
+++ b/gcc/config/arm/unwind-arm.c
@@ -27,6 +27,10 @@
Boston, MA 02110-1301, USA. */
#include "unwind.h"
+/* We add a prototype for abort here to avoid creating a dependency on
+ target headers. */
+extern void abort (void);
+
/* Definitions for C++ runtime support routines. We make these weak
declarations to avoid pulling in libsupc++ unnecessarily. */
typedef unsigned char bool;
@@ -54,7 +58,7 @@ __gnu_Unwind_Find_exidx (_Unwind_Ptr, int *);
#define UCB_FORCED_STOP_FN(ucbp) ((ucbp)->unwinder_cache.reserved1)
#define UCB_PR_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved2)
#define UCB_SAVED_CALLSITE_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved3)
-#define UCB_FORCED_STOP_ARG(ucb) ((ucbp)->unwinder_cache.reserved4)
+#define UCB_FORCED_STOP_ARG(ucbp) ((ucbp)->unwinder_cache.reserved4)
struct core_regs
{
@@ -107,6 +111,7 @@ typedef struct
/* The first fields must be the same as a phase2_vrs. */
_uw demand_save_flags;
struct core_regs core;
+ _uw prev_sp; /* Only valid during forced unwinding. */
struct vfp_regs vfp;
struct fpa_regs fpa;
} phase1_vrs;
@@ -497,11 +502,21 @@ unwind_phase2 (_Unwind_Control_Block * ucbp, phase2_vrs * vrs)
/* Perform phase2 forced unwinding. */
static _Unwind_Reason_Code
-unwind_phase2_forced (_Unwind_Control_Block *ucbp, phase2_vrs *entry_vrs)
+unwind_phase2_forced (_Unwind_Control_Block *ucbp, phase2_vrs *entry_vrs,
+ int resuming)
{
_Unwind_Stop_Fn stop_fn = (_Unwind_Stop_Fn) UCB_FORCED_STOP_FN (ucbp);
void *stop_arg = (void *)UCB_FORCED_STOP_ARG (ucbp);
- _Unwind_Reason_Code pr_result;
+ _Unwind_Reason_Code pr_result = 0;
+ /* We use phase1_vrs here even though we do not demand save, for the
+ prev_sp field. */
+ phase1_vrs saved_vrs, next_vrs;
+
+ /* Save the core registers. */
+ saved_vrs.core = entry_vrs->core;
+ /* We don't need to demand-save the non-core registers, because we
+ unwind in a single pass. */
+ saved_vrs.demand_save_flags = 0;
/* Unwind until we reach a propagation barrier. */
do
@@ -511,27 +526,51 @@ unwind_phase2_forced (_Unwind_Control_Block *ucbp, phase2_vrs *entry_vrs)
_Unwind_Reason_Code stop_code;
/* Find the entry for this routine. */
- entry_code = get_eit_entry (ucbp, entry_vrs->core.r[R_PC]);
+ entry_code = get_eit_entry (ucbp, saved_vrs.core.r[R_PC]);
- action = _US_UNWIND_FRAME_STARTING | _US_FORCE_UNWIND;
- if (entry_code == _URC_END_OF_STACK)
- action |= _US_END_OF_STACK;
- else if (entry_code != _URC_OK)
- return _URC_FAILURE;
+ if (resuming)
+ {
+ action = _US_UNWIND_FRAME_RESUME | _US_FORCE_UNWIND;
+ resuming = 0;
+ }
+ else
+ action = _US_UNWIND_FRAME_STARTING | _US_FORCE_UNWIND;
+
+ if (entry_code == _URC_OK)
+ {
+ UCB_SAVED_CALLSITE_ADDR (ucbp) = saved_vrs.core.r[R_PC];
+
+ next_vrs = saved_vrs;
+
+ /* Call the pr to decide what to do. */
+ pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
+ (action, ucbp, (void *) &next_vrs);
+
+ saved_vrs.prev_sp = next_vrs.core.r[R_SP];
+ }
+ else
+ {
+ /* Treat any failure as the end of unwinding, to cope more
+ gracefully with missing EH information. Mixed EH and
+ non-EH within one object will usually result in failure,
+ because the .ARM.exidx tables do not indicate the end
+ of the code to which they apply; but mixed EH and non-EH
+ shared objects should return an unwind failure at the
+ entry of a non-EH shared object. */
+ action |= _US_END_OF_STACK;
+
+ saved_vrs.prev_sp = saved_vrs.core.r[R_SP];
+ }
stop_code = stop_fn (1, action, ucbp->exception_class, ucbp,
- (void *)entry_vrs, stop_arg);
+ (void *)&saved_vrs, stop_arg);
if (stop_code != _URC_NO_REASON)
return _URC_FAILURE;
- if (entry_code == _URC_END_OF_STACK)
+ if (entry_code != _URC_OK)
return entry_code;
- UCB_SAVED_CALLSITE_ADDR (ucbp) = entry_vrs->core.r[R_PC];
-
- /* Call the pr to decide what to do. */
- pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
- (action, ucbp, (void *) entry_vrs);
+ saved_vrs = next_vrs;
}
while (pr_result == _URC_CONTINUE_UNWIND);
@@ -542,7 +581,20 @@ unwind_phase2_forced (_Unwind_Control_Block *ucbp, phase2_vrs *entry_vrs)
return _URC_FAILURE;
}
- restore_core_regs (&entry_vrs->core);
+ restore_core_regs (&saved_vrs.core);
+}
+
+/* This is a very limited implementation of _Unwind_GetCFA. It returns
+ the stack pointer as it is about to be unwound, and is only valid
+ while calling the stop function during forced unwinding. If the
+ current personality routine result is going to run a cleanup, this
+ will not be the CFA; but when the frame is really unwound, it will
+ be. */
+
+_Unwind_Word
+_Unwind_GetCFA (_Unwind_Context *context)
+{
+ return ((phase1_vrs *) context)->prev_sp;
}
/* Perform phase1 unwinding. UCBP is the exception being thrown, and
@@ -610,7 +662,7 @@ __gnu_Unwind_ForcedUnwind (_Unwind_Control_Block *ucbp,
/* Set the pc to the call site. */
entry_vrs->core.r[R_PC] = entry_vrs->core.r[R_LR];
- return unwind_phase2_forced (ucbp, entry_vrs);
+ return unwind_phase2_forced (ucbp, entry_vrs, 0);
}
_Unwind_Reason_Code
@@ -620,18 +672,21 @@ _Unwind_Reason_Code
__gnu_Unwind_Resume (_Unwind_Control_Block * ucbp, phase2_vrs * entry_vrs)
{
_Unwind_Reason_Code pr_result;
- _Unwind_State action;
/* Recover the saved address. */
entry_vrs->core.r[R_PC] = UCB_SAVED_CALLSITE_ADDR (ucbp);
- /* Call the cached PR. */
- action = _US_UNWIND_FRAME_RESUME;
if (UCB_FORCED_STOP_FN (ucbp))
- action |= _US_FORCE_UNWIND;
+ {
+ unwind_phase2_forced (ucbp, entry_vrs, 1);
+ /* We can't return failure at this point. */
+ abort ();
+ }
+
+ /* Call the cached PR. */
pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
- (action, ucbp, (_Unwind_Context *) entry_vrs);
+ (_US_UNWIND_FRAME_RESUME, ucbp, (_Unwind_Context *) entry_vrs);
switch (pr_result)
{
@@ -641,10 +696,7 @@ __gnu_Unwind_Resume (_Unwind_Control_Block * ucbp, phase2_vrs * entry_vrs)
case _URC_CONTINUE_UNWIND:
/* Continue unwinding the next frame. */
- if (UCB_FORCED_STOP_FN (ucbp))
- return unwind_phase2_forced (ucbp, entry_vrs);
- else
- unwind_phase2 (ucbp, entry_vrs);
+ unwind_phase2 (ucbp, entry_vrs);
default:
abort ();
@@ -664,7 +716,7 @@ __gnu_Unwind_Resume_or_Rethrow (_Unwind_Control_Block * ucbp,
/* Set the pc to the call site. */
entry_vrs->core.r[R_PC] = entry_vrs->core.r[R_LR];
/* Continue unwinding the next frame. */
- return unwind_phase2_forced (ucbp, entry_vrs);
+ return unwind_phase2_forced (ucbp, entry_vrs, 0);
}
/* Clean up an exception object when unwinding is complete. */
@@ -947,3 +999,16 @@ __aeabi_unwind_cpp_pr2 (_Unwind_State state,
{
return __gnu_unwind_pr_common (state, ucbp, context, 2);
}
+
+/* These two should never be used. */
+_Unwind_Ptr
+_Unwind_GetDataRelBase (_Unwind_Context *context __attribute__ ((unused)))
+{
+ abort ();
+}
+
+_Unwind_Ptr
+_Unwind_GetTextRelBase (_Unwind_Context *context __attribute__ ((unused)))
+{
+ abort ();
+}