Skip to content

Commit

Permalink
[libunwind][ARM] Handle end of stack during unwind
Browse files Browse the repository at this point in the history
When unwind step reaches the end of the stack that means the force unwind should notify the stop function.
This is not an error, it could mean just the thread is cleaned up completely.

Reviewed By: #libunwind, mstorsjo

Differential Revision: https://reviews.llvm.org/D109856
  • Loading branch information
DanielKristofKiss committed Nov 26, 2021
1 parent 8967d04 commit 632acec
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 7 deletions.
9 changes: 7 additions & 2 deletions libcxxabi/src/cxa_personality.cpp
Expand Up @@ -1004,9 +1004,14 @@ extern "C" _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception*,
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
Expand Down
79 changes: 79 additions & 0 deletions 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 <assert.h>
#include <exception>
#include <stdlib.h>
#include <string.h>
#include <unwind.h>
#include <thread>
#include <tuple>
#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 <typename T>
struct Stop;

template <typename R, typename... Args>
struct Stop<R (*)(Args...)> {
// 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<Args...>>::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
29 changes: 24 additions & 5 deletions libunwind/src/Unwind-EHABI.cpp
Expand Up @@ -187,9 +187,14 @@ static _Unwind_Reason_Code unwindOneFrame(_Unwind_State state,
if (result != _URC_CONTINUE_UNWIND)
return result;

if (__unw_step(reinterpret_cast<unw_cursor_t *>(context)) != UNW_STEP_SUCCESS)
switch (__unw_step(reinterpret_cast<unw_cursor_t *>(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 /
Expand Down Expand Up @@ -678,12 +683,13 @@ 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) {
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<void *>(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) {
Expand Down Expand Up @@ -756,6 +762,14 @@ unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor,
// 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): "
Expand Down Expand Up @@ -1133,9 +1147,14 @@ extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code
__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)

0 comments on commit 632acec

Please sign in to comment.