diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp --- a/libcxxabi/src/cxa_personality.cpp +++ b/libcxxabi/src/cxa_personality.cpp @@ -1004,9 +1004,14 @@ static _Unwind_Reason_Code continue_unwind(_Unwind_Exception* unwind_exception, _Unwind_Context* context) { - if (__gnu_unwind_frame(unwind_exception, context) != _URC_OK) - return _URC_FAILURE; + switch (__gnu_unwind_frame(unwind_exception, context)) { + case _URC_OK: return _URC_CONTINUE_UNWIND; + case _URC_END_OF_STACK: + return _URC_END_OF_STACK; + default: + return _URC_FAILURE; + } } // ARM register names diff --git a/libcxxabi/test/forced_unwind3.pass.cpp b/libcxxabi/test/forced_unwind3.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxxabi/test/forced_unwind3.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Let's run ForcedUnwind until it reaches end of the stack, this test simulates +// what pthread_cancel does. + +// UNSUPPORTED: c++03 +// UNSUPPORTED: libcxxabi-no-threads +// UNSUPPORTED: no-exceptions + +#include +#include +#include +#include +#include +#include +#include +#include <__cxxabi_config.h> + +// TODO: dump version back to 14 once clang is updated on the CI. +#if defined(_LIBCXXABI_ARM_EHABI) && defined(__clang__) && __clang_major__ < 15 +// _Unwind_ForcedUnwind is not available or broken before version 14. +int main(int, char**) { return 0; } + +#else +static bool destructorCalled = false; + +struct myClass { + myClass() {} + ~myClass() { + assert(destructorCalled == false); + destructorCalled = true; + }; +}; + +template +struct Stop; + +template +struct Stop { + // The third argument of _Unwind_Stop_Fn is uint64_t in Itanium C++ ABI/LLVM + // libunwind while _Unwind_Exception_Class in libgcc. + typedef typename std::tuple_element<2, std::tuple>::type type; + + static _Unwind_Reason_Code stop(int, _Unwind_Action actions, type, struct _Unwind_Exception*, struct _Unwind_Context*, + void*) { + if (actions & _UA_END_OF_STACK) { + assert(destructorCalled == true); + exit(0); + } + return _URC_NO_REASON; + } +}; + +static void forced_unwind() { + _Unwind_Exception* exc = new _Unwind_Exception; + memset(&exc->exception_class, 0, sizeof(exc->exception_class)); + exc->exception_cleanup = 0; + _Unwind_ForcedUnwind(exc, Stop<_Unwind_Stop_Fn>::stop, 0); + abort(); +} + +__attribute__((__noinline__)) static void test() { + myClass c{}; + forced_unwind(); + abort(); +} + +int main(int, char**) { + std::thread t{test}; + t.join(); + return -1; +} +#endif diff --git a/libunwind/src/Unwind-EHABI.cpp b/libunwind/src/Unwind-EHABI.cpp --- a/libunwind/src/Unwind-EHABI.cpp +++ b/libunwind/src/Unwind-EHABI.cpp @@ -187,9 +187,14 @@ if (result != _URC_CONTINUE_UNWIND) return result; - if (__unw_step(reinterpret_cast(context)) != UNW_STEP_SUCCESS) + switch (__unw_step(reinterpret_cast(context))) { + case UNW_STEP_SUCCESS: + return _URC_CONTINUE_UNWIND; + case UNW_STEP_END: + return _URC_END_OF_STACK; + default: return _URC_FAILURE; - return _URC_CONTINUE_UNWIND; + } } // Generates mask discriminator for _Unwind_VRS_Pop, e.g. for _UVRSC_CORE / @@ -678,12 +683,13 @@ unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, void *stop_parameter) { + bool endOfStack = false; // 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(exception_object)); // Walk each frame until we reach where search phase said to stop - while (true) { + while (!endOfStack) { // Update info about this frame. unw_proc_info_t frameInfo; if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { @@ -756,6 +762,14 @@ // We may get control back if landing pad calls _Unwind_Resume(). __unw_resume(cursor); break; + case _URC_END_OF_STACK: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_END_OF_STACK", + (void *)exception_object); + // Personalty routine did the step and it can't step forward. + endOfStack = true; + break; default: // Personality routine returned an unknown result code. _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " @@ -1133,9 +1147,14 @@ __gnu_unwind_frame(_Unwind_Exception *exception_object, struct _Unwind_Context *context) { unw_cursor_t *cursor = (unw_cursor_t *)context; - if (__unw_step(cursor) != UNW_STEP_SUCCESS) + switch (__unw_step(cursor)) { + case UNW_STEP_SUCCESS: + return _URC_OK; + case UNW_STEP_END: + return _URC_END_OF_STACK; + default: return _URC_FAILURE; - return _URC_OK; + } } #endif // defined(_LIBUNWIND_ARM_EHABI)