blob: 2e30f51e71cb1bf394bbf4a77a029bbd72f8d1df [file] [log] [blame]
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;
+}