| commit db126ae243cd70e4f68fd50a7c619740e90e1dc6 |
| Author: Daniel Kiss <daniel.kiss@arm.com> |
| Date: Wed Aug 11 10:11:30 2021 +0200 |
| |
| [Arm][Unwind][libc++abi] Add _Unwind_ForcedUnwind to EHABI. |
| |
| _Unwind_ForcedUnwind is not mandated by the EHABI but for compatibilty |
| reasons adding so the interface to higher layers would be the same. |
| Dropping EHABI specific _Unwind_Stop_Fn definition since it is not defined by EHABI. |
| |
| Reviewed By: MaskRay |
| |
| Differential Revision: https://reviews.llvm.org/D89570 |
| |
| diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp |
| index a4f81d74735f..d63741b19b3d 100644 |
| --- a/libcxxabi/src/cxa_personality.cpp |
| +++ b/libcxxabi/src/cxa_personality.cpp |
| @@ -1109,7 +1109,14 @@ __gxx_personality_v0(_Unwind_State state, |
| // Either we didn't do a phase 1 search (due to forced unwinding), or |
| // phase 1 reported no catching-handlers. |
| // Search for a (non-catching) cleanup |
| - scan_eh_tab(results, _UA_CLEANUP_PHASE, native_exception, unwind_exception, context); |
| + if (is_force_unwinding) |
| + scan_eh_tab( |
| + results, |
| + static_cast<_Unwind_Action>(_UA_CLEANUP_PHASE | _UA_FORCE_UNWIND), |
| + native_exception, unwind_exception, context); |
| + else |
| + scan_eh_tab(results, _UA_CLEANUP_PHASE, native_exception, |
| + unwind_exception, context); |
| if (results.reason == _URC_HANDLER_FOUND) |
| { |
| // Found a non-catching handler |
| diff --git a/libcxxabi/test/forced_unwind1.pass.cpp b/libcxxabi/test/forced_unwind1.pass.cpp |
| index 69f93ffaacc0..b6963a024299 100644 |
| --- a/libcxxabi/test/forced_unwind1.pass.cpp |
| +++ b/libcxxabi/test/forced_unwind1.pass.cpp |
| @@ -20,11 +20,6 @@ |
| #include <tuple> |
| #include <__cxxabi_config.h> |
| |
| -#if defined(_LIBCXXABI_ARM_EHABI) |
| -int main(int, char**) { |
| - return 0; |
| -} |
| -#else |
| static int bits = 0; |
| |
| struct C { |
| @@ -84,4 +79,3 @@ int main(int, char**) { |
| test(); |
| return bits != 15; |
| } |
| -#endif |
| diff --git a/libcxxabi/test/forced_unwind2.pass.cpp b/libcxxabi/test/forced_unwind2.pass.cpp |
| index cb527581687a..037f0499282f 100644 |
| --- a/libcxxabi/test/forced_unwind2.pass.cpp |
| +++ b/libcxxabi/test/forced_unwind2.pass.cpp |
| @@ -21,11 +21,6 @@ |
| #include <tuple> |
| #include <__cxxabi_config.h> |
| |
| -#if defined(_LIBCXXABI_ARM_EHABI) |
| -int main(int, char**) { |
| - return 0; |
| -} |
| -#else |
| template <typename T> |
| struct Stop; |
| |
| @@ -64,4 +59,3 @@ int main(int, char**) { |
| } |
| abort(); |
| } |
| -#endif |
| diff --git a/libunwind/include/unwind.h b/libunwind/include/unwind.h |
| index e8d114854325..87c3cf6c804e 100644 |
| --- a/libunwind/include/unwind.h |
| +++ b/libunwind/include/unwind.h |
| @@ -61,6 +61,14 @@ typedef struct _Unwind_Context _Unwind_Context; // opaque |
| #include "unwind_itanium.h" |
| #endif |
| |
| +typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) |
| + (int version, |
| + _Unwind_Action actions, |
| + uint64_t exceptionClass, |
| + _Unwind_Exception* exceptionObject, |
| + struct _Unwind_Context* context, |
| + void* stop_parameter); |
| + |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| diff --git a/libunwind/include/unwind_arm_ehabi.h b/libunwind/include/unwind_arm_ehabi.h |
| index 58444d14eb8d..5ad088722560 100644 |
| --- a/libunwind/include/unwind_arm_ehabi.h |
| +++ b/libunwind/include/unwind_arm_ehabi.h |
| @@ -26,7 +26,7 @@ typedef uint32_t _Unwind_EHT_Header; |
| |
| struct _Unwind_Control_Block; |
| typedef struct _Unwind_Control_Block _Unwind_Control_Block; |
| -typedef struct _Unwind_Control_Block _Unwind_Exception; /* Alias */ |
| +#define _Unwind_Exception _Unwind_Control_Block /* Alias */ |
| |
| struct _Unwind_Control_Block { |
| uint64_t exception_class; |
| @@ -63,11 +63,6 @@ struct _Unwind_Control_Block { |
| long long int :0; /* Enforce the 8-byte alignment */ |
| } __attribute__((__aligned__(8))); |
| |
| -typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) |
| - (_Unwind_State state, |
| - _Unwind_Exception* exceptionObject, |
| - struct _Unwind_Context* context); |
| - |
| typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)( |
| _Unwind_State state, _Unwind_Exception *exceptionObject, |
| struct _Unwind_Context *context); |
| diff --git a/libunwind/include/unwind_itanium.h b/libunwind/include/unwind_itanium.h |
| index 1e1389c7f0da..eeb45f622832 100644 |
| --- a/libunwind/include/unwind_itanium.h |
| +++ b/libunwind/include/unwind_itanium.h |
| @@ -39,14 +39,6 @@ struct _Unwind_Exception { |
| // alignment for the target"; so do we. |
| } __attribute__((__aligned__)); |
| |
| -typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) |
| - (int version, |
| - _Unwind_Action actions, |
| - uint64_t exceptionClass, |
| - _Unwind_Exception* exceptionObject, |
| - struct _Unwind_Context* context, |
| - void* stop_parameter ); |
| - |
| typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)( |
| int version, _Unwind_Action actions, uint64_t exceptionClass, |
| _Unwind_Exception *exceptionObject, struct _Unwind_Context *context); |
| diff --git a/libunwind/src/Unwind-EHABI.cpp b/libunwind/src/Unwind-EHABI.cpp |
| index 8843db7f54c3..ba6064d3ef00 100644 |
| --- a/libunwind/src/Unwind-EHABI.cpp |
| +++ b/libunwind/src/Unwind-EHABI.cpp |
| @@ -602,7 +602,7 @@ static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor |
| // If there is a personality routine, tell it we are unwinding. |
| if (frameInfo.handler != 0) { |
| _Unwind_Personality_Fn p = |
| - (_Unwind_Personality_Fn)(long)(frameInfo.handler); |
| + (_Unwind_Personality_Fn)(intptr_t)(frameInfo.handler); |
| struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor); |
| // EHABI #7.2 |
| exception_object->pr_cache.fnstart = frameInfo.start_ip; |
| @@ -670,6 +670,112 @@ static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor |
| return _URC_FATAL_PHASE2_ERROR; |
| } |
| |
| +static _Unwind_Reason_Code |
| +unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, |
| + _Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, |
| + void *stop_parameter) { |
| + // See comment at the start of unwind_phase1 regarding VRS integrity. |
| + __unw_init_local(cursor, uc); |
| + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_force(ex_ojb=%p)", |
| + static_cast<void *>(exception_object)); |
| + // Walk each frame until we reach where search phase said to stop |
| + while (true) { |
| + // Update info about this frame. |
| + unw_proc_info_t frameInfo; |
| + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { |
| + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step " |
| + "failed => _URC_END_OF_STACK", |
| + (void *)exception_object); |
| + return _URC_FATAL_PHASE2_ERROR; |
| + } |
| + |
| + // When tracing, print state information. |
| + if (_LIBUNWIND_TRACING_UNWINDING) { |
| + char functionBuf[512]; |
| + const char *functionName = functionBuf; |
| + unw_word_t offset; |
| + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), |
| + &offset) != UNW_ESUCCESS) || |
| + (frameInfo.start_ip + offset > frameInfo.end_ip)) |
| + functionName = ".anonymous."; |
| + _LIBUNWIND_TRACE_UNWINDING( |
| + "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIxPTR |
| + ", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR, |
| + (void *)exception_object, frameInfo.start_ip, functionName, |
| + frameInfo.lsda, frameInfo.handler); |
| + } |
| + |
| + // Call stop function at each frame. |
| + _Unwind_Action action = |
| + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); |
| + _Unwind_Reason_Code stopResult = |
| + (*stop)(1, action, exception_object->exception_class, exception_object, |
| + (_Unwind_Context *)(cursor), stop_parameter); |
| + _LIBUNWIND_TRACE_UNWINDING( |
| + "unwind_phase2_forced(ex_ojb=%p): stop function returned %d", |
| + (void *)exception_object, stopResult); |
| + if (stopResult != _URC_NO_REASON) { |
| + _LIBUNWIND_TRACE_UNWINDING( |
| + "unwind_phase2_forced(ex_ojb=%p): stopped by stop function", |
| + (void *)exception_object); |
| + return _URC_FATAL_PHASE2_ERROR; |
| + } |
| + |
| + // If there is a personality routine, tell it we are unwinding. |
| + if (frameInfo.handler != 0) { |
| + _Unwind_Personality_Fn p = |
| + (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler); |
| + struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor); |
| + // EHABI #7.2 |
| + exception_object->pr_cache.fnstart = frameInfo.start_ip; |
| + exception_object->pr_cache.ehtp = |
| + (_Unwind_EHT_Header *)frameInfo.unwind_info; |
| + exception_object->pr_cache.additional = frameInfo.flags; |
| + _Unwind_Reason_Code personalityResult = |
| + (*p)(_US_FORCE_UNWIND | _US_UNWIND_FRAME_STARTING, exception_object, |
| + context); |
| + switch (personalityResult) { |
| + case _URC_CONTINUE_UNWIND: |
| + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " |
| + "personality returned " |
| + "_URC_CONTINUE_UNWIND", |
| + (void *)exception_object); |
| + // Destructors called, continue unwinding |
| + break; |
| + case _URC_INSTALL_CONTEXT: |
| + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " |
| + "personality returned " |
| + "_URC_INSTALL_CONTEXT", |
| + (void *)exception_object); |
| + // We may get control back if landing pad calls _Unwind_Resume(). |
| + __unw_resume(cursor); |
| + break; |
| + default: |
| + // Personality routine returned an unknown result code. |
| + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " |
| + "personality returned %d, " |
| + "_URC_FATAL_PHASE2_ERROR", |
| + (void *)exception_object, personalityResult); |
| + return _URC_FATAL_PHASE2_ERROR; |
| + } |
| + } |
| + } |
| + |
| + // Call stop function one last time and tell it we've reached the end |
| + // of the stack. |
| + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " |
| + "function with _UA_END_OF_STACK", |
| + (void *)exception_object); |
| + _Unwind_Action lastAction = |
| + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); |
| + (*stop)(1, lastAction, exception_object->exception_class, exception_object, |
| + (struct _Unwind_Context *)(cursor), stop_parameter); |
| + |
| + // Clean up phase did not resume at the frame that the search phase said it |
| + // would. |
| + return _URC_FATAL_PHASE2_ERROR; |
| +} |
| + |
| /// Called by __cxa_throw. Only returns if there is a fatal error. |
| _LIBUNWIND_EXPORT _Unwind_Reason_Code |
| _Unwind_RaiseException(_Unwind_Exception *exception_object) { |
| @@ -717,10 +823,13 @@ _Unwind_Resume(_Unwind_Exception *exception_object) { |
| unw_cursor_t cursor; |
| __unw_getcontext(&uc); |
| |
| - // _Unwind_RaiseException on EHABI will always set the reserved1 field to 0, |
| - // which is in the same position as private_1 below. |
| - // TODO(ajwong): Who wronte the above? Why is it true? |
| - unwind_phase2(&uc, &cursor, exception_object, true); |
| + if (exception_object->unwinder_cache.reserved1) |
| + unwind_phase2_forced( |
| + &uc, &cursor, exception_object, |
| + (_Unwind_Stop_Fn)exception_object->unwinder_cache.reserved1, |
| + (void *)exception_object->unwinder_cache.reserved3); |
| + else |
| + unwind_phase2(&uc, &cursor, exception_object, true); |
| |
| // Clients assume _Unwind_Resume() does not return, so all we can do is abort. |
| _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); |
| @@ -967,6 +1076,27 @@ _Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, |
| _LIBUNWIND_ABORT("unsupported register class"); |
| } |
| |
| +/// Not used by C++. |
| +/// Unwinds stack, calling "stop" function at each frame. |
| +/// Could be used to implement longjmp(). |
| +_LIBUNWIND_EXPORT _Unwind_Reason_Code |
| +_Unwind_ForcedUnwind(_Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, |
| + void *stop_parameter) { |
| + _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)", |
| + (void *)exception_object, (void *)(uintptr_t)stop); |
| + unw_context_t uc; |
| + unw_cursor_t cursor; |
| + __unw_getcontext(&uc); |
| + |
| + // Mark that this is a forced unwind, so _Unwind_Resume() can do |
| + // the right thing. |
| + exception_object->unwinder_cache.reserved1 = (uintptr_t)stop; |
| + exception_object->unwinder_cache.reserved3 = (uintptr_t)stop_parameter; |
| + |
| + return unwind_phase2_forced(&uc, &cursor, exception_object, stop, |
| + stop_parameter); |
| +} |
| + |
| /// Called by personality handler during phase 2 to find the start of the |
| /// function. |
| _LIBUNWIND_EXPORT uintptr_t |
| diff --git a/libunwind/src/UnwindLevel1-gcc-ext.c b/libunwind/src/UnwindLevel1-gcc-ext.c |
| index 310b836d129e..d69267ba25fe 100644 |
| --- a/libunwind/src/UnwindLevel1-gcc-ext.c |
| +++ b/libunwind/src/UnwindLevel1-gcc-ext.c |
| @@ -25,31 +25,24 @@ |
| #if defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) |
| |
| #if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) |
| -#define private_1 private_[0] |
| +#define PRIVATE_1 private_[0] |
| +#elif defined(_LIBUNWIND_ARM_EHABI) |
| +#define PRIVATE_1 unwinder_cache.reserved1 |
| +#else |
| +#define PRIVATE_1 private_1 |
| #endif |
| |
| /// Called by __cxa_rethrow(). |
| _LIBUNWIND_EXPORT _Unwind_Reason_Code |
| _Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) { |
| -#if defined(_LIBUNWIND_ARM_EHABI) |
| - _LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%ld", |
| - (void *)exception_object, |
| - (long)exception_object->unwinder_cache.reserved1); |
| -#else |
| - _LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%" PRIdPTR, |
| - (void *)exception_object, |
| - (intptr_t)exception_object->private_1); |
| -#endif |
| + _LIBUNWIND_TRACE_API( |
| + "_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%" PRIdPTR, |
| + (void *)exception_object, (intptr_t)exception_object->PRIVATE_1); |
| |
| -#if defined(_LIBUNWIND_ARM_EHABI) |
| - // _Unwind_RaiseException on EHABI will always set the reserved1 field to 0, |
| - // which is in the same position as private_1 below. |
| - return _Unwind_RaiseException(exception_object); |
| -#else |
| // If this is non-forced and a stopping place was found, then this is a |
| // re-throw. |
| // Call _Unwind_RaiseException() as if this was a new exception |
| - if (exception_object->private_1 == 0) { |
| + if (exception_object->PRIVATE_1 == 0) { |
| return _Unwind_RaiseException(exception_object); |
| // Will return if there is no catch clause, so that __cxa_rethrow can call |
| // std::terminate(). |
| @@ -60,10 +53,8 @@ _Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) { |
| _Unwind_Resume(exception_object); |
| _LIBUNWIND_ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException()" |
| " which unexpectedly returned"); |
| -#endif |
| } |
| |
| - |
| /// Called by personality handler during phase 2 to get base address for data |
| /// relative encodings. |
| _LIBUNWIND_EXPORT uintptr_t |
| diff --git a/libunwind/test/forceunwind.pass.cpp b/libunwind/test/forceunwind.pass.cpp |
| new file mode 100644 |
| index 000000000000..e74aa3faa080 |
| --- /dev/null |
| +++ b/libunwind/test/forceunwind.pass.cpp |
| @@ -0,0 +1,68 @@ |
| +// -*- C++ -*- |
| +//===----------------------------------------------------------------------===// |
| +// |
| +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| +// See https://llvm.org/LICENSE.txt for license information. |
| +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| +// |
| +//===----------------------------------------------------------------------===// |
| + |
| +// REQUIRES: linux |
| + |
| +// Basic test for _Unwind_ForcedUnwind. |
| +// See libcxxabi/test/forced_unwind* tests too. |
| + |
| +#include <assert.h> |
| +#include <dlfcn.h> |
| +#include <signal.h> |
| +#include <stdint.h> |
| +#include <stdio.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| +#include <sys/types.h> |
| +#include <unistd.h> |
| +#include <unwind.h> |
| + |
| +void foo(); |
| +_Unwind_Exception ex; |
| + |
| +_Unwind_Reason_Code stop(int version, _Unwind_Action actions, |
| + uint64_t exceptionClass, |
| + _Unwind_Exception *exceptionObject, |
| + struct _Unwind_Context *context, |
| + void *stop_parameter) { |
| + assert(version == 1); |
| + assert((actions & _UA_FORCE_UNWIND) != 0); |
| + (void)exceptionClass; |
| + assert(exceptionObject == &ex); |
| + assert(stop_parameter == &foo); |
| + |
| + Dl_info info = {0, 0, 0, 0}; |
| + |
| + // Unwind util the main is reached, above frames depend on the platform and |
| + // architecture. |
| + if (dladdr(reinterpret_cast<void *>(_Unwind_GetIP(context)), &info) && |
| + info.dli_sname && !strcmp("main", info.dli_sname)) { |
| + _Exit(0); |
| + } |
| + return _URC_NO_REASON; |
| +} |
| + |
| +__attribute__((noinline)) void foo() { |
| + |
| + // Arm EHABI defines struct _Unwind_Control_Block as exception |
| + // object. Ensure struct _Unwind_Exception* work there too, |
| + // because _Unwind_Exception in this case is just an alias. |
| + struct _Unwind_Exception *e = &ex; |
| +#if defined(_LIBUNWIND_ARM_EHABI) |
| + // Create a mock exception object. |
| + memset(e, '\0', sizeof(*e)); |
| + e->exception_class = 0x434C4E47554E5700; // CLNGUNW\0 |
| +#endif |
| + _Unwind_ForcedUnwind(e, stop, (void *)&foo); |
| +} |
| + |
| +int main() { |
| + foo(); |
| + return -2; |
| +} |