From db126ae243cd70e4f68fd50a7c619740e90e1dc6 Mon Sep 17 00:00:00 2001 From: Daniel Kiss Date: Wed, 11 Aug 2021 10:11:30 +0200 Subject: [PATCH] [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 --- libcxxabi/src/cxa_personality.cpp | 9 ++- libcxxabi/test/forced_unwind1.pass.cpp | 6 -- libcxxabi/test/forced_unwind2.pass.cpp | 6 -- libunwind/include/unwind.h | 8 ++ libunwind/include/unwind_arm_ehabi.h | 7 +- libunwind/include/unwind_itanium.h | 8 -- libunwind/src/Unwind-EHABI.cpp | 140 +++++++++++++++++++++++++++++++-- libunwind/src/UnwindLevel1-gcc-ext.c | 27 +++---- libunwind/test/forceunwind.pass.cpp | 68 ++++++++++++++++ 9 files changed, 229 insertions(+), 50 deletions(-) create mode 100644 libunwind/test/forceunwind.pass.cpp diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp index a4f81d7..d63741b 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 69f93ff..b6963a0 100644 --- a/libcxxabi/test/forced_unwind1.pass.cpp +++ b/libcxxabi/test/forced_unwind1.pass.cpp @@ -20,11 +20,6 @@ #include #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 cb52758..037f049 100644 --- a/libcxxabi/test/forced_unwind2.pass.cpp +++ b/libcxxabi/test/forced_unwind2.pass.cpp @@ -21,11 +21,6 @@ #include #include <__cxxabi_config.h> -#if defined(_LIBCXXABI_ARM_EHABI) -int main(int, char**) { - return 0; -} -#else template 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 e8d1148..87c3cf6 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 58444d1..5ad0887 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 1e1389c..eeb45f6 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 8843db7..ba6064d 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(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 310b836..d69267ba 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 0000000..e74aa3f --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(_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; +} -- 2.7.4