[libunwind][test][AIX] Add C API test for unwinding from AIX VAPI (as signal handler) (#209662)
Further to https://github.com/llvm/llvm-project/pull/209306, test a case where a signal handler is a Virtual API function triggered synchronously while the VAPI is not active. Resuming an ancestor context of the signal frame should call the VAPI return glue. --------- Assisted-by: IBM Bob
This commit is contained in:
parent
f266ca172c
commit
dbaabbc920
1 changed files with 115 additions and 0 deletions
115
libunwind/test/aix_vapi_signal_unwind.pass.cpp
Normal file
115
libunwind/test/aix_vapi_signal_unwind.pass.cpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Tests unwinding where the signal handler is a VAPI function.
|
||||
//
|
||||
/// `exit` is used as the signal handler and the unwinding is done in an atexit
|
||||
/// handler.
|
||||
/// `__builtin_debugtrap` is used because `raise` is also a VAPI function.
|
||||
|
||||
// REQUIRES: target={{.+}}-aix{{.*}}
|
||||
// REQUIRES: has-filecheck
|
||||
|
||||
// ADDITIONAL_COMPILE_FLAGS: -fno-inline -fno-exceptions
|
||||
|
||||
// RUN: %{build}
|
||||
// RUN: %{exec} %t.exe 2>&1 \
|
||||
// RUN: | FileCheck --check-prefix=CHECK \
|
||||
// RUN: %if libunwind-assertions-enabled %{ --check-prefix=DEBUG %} \
|
||||
// RUN: %s
|
||||
|
||||
#include <errno.h>
|
||||
#include <libunwind.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/// VAPI glue addresses
|
||||
constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8b80;
|
||||
constexpr uintptr_t vapi_addr_64 = 0x8e00;
|
||||
constexpr size_t vapi_size_64 = 0x0200;
|
||||
constexpr uintptr_t vapi_glue_addr_begin = vapi_glue_addr_ext_32;
|
||||
constexpr uintptr_t vapi_glue_addr_end = vapi_addr_64 + vapi_size_64;
|
||||
|
||||
struct FunctionDescriptor {
|
||||
uintptr_t entry;
|
||||
uintptr_t toc;
|
||||
uintptr_t env;
|
||||
};
|
||||
|
||||
void my_atexit_handler(void) {
|
||||
FunctionDescriptor *fd = reinterpret_cast<FunctionDescriptor *>(exit);
|
||||
bool llu_enabled =
|
||||
vapi_glue_addr_begin <= fd->entry && fd->entry < vapi_glue_addr_end;
|
||||
|
||||
fprintf(stderr, "Retrieve a cursor to `main` by stepping up.\n");
|
||||
// CHECK-LABEL: Retrieve a cursor to `main`
|
||||
unw_context_t context;
|
||||
unw_cursor_t cursor;
|
||||
unw_getcontext(&context);
|
||||
unw_init_local(&cursor, &context);
|
||||
/// Step from `my_atexit_handler` up to `exit`.
|
||||
unw_step(&cursor);
|
||||
if (!llu_enabled)
|
||||
fprintf(stderr,
|
||||
"libunwind: the next return address=VAPI_NOT_ENABLED from VAPI\n");
|
||||
/// Note:
|
||||
/// The synthetic VAPI_NOT_ENABLED output would appear _after_ the trace
|
||||
/// output indicating that the "next is a signal handler frame". Use DEBUG-DAG
|
||||
/// to allow for that.
|
||||
// DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=_Z17my_atexit_handlerv
|
||||
// DEBUG-DAG: libunwind: the next return address={{[^ ]*}} from VAPI
|
||||
// DEBUG-DAG: libunwind: The next is a signal handler frame
|
||||
/// Step from `exit` (signal handler, VAPI) up to `trapper`.
|
||||
unw_step(&cursor);
|
||||
if (!llu_enabled)
|
||||
fprintf(stderr,
|
||||
"libunwind: The return address in stack VAPI_NOT_ENABLED is within "
|
||||
"the range of VAPI address; set isKnownVapiNotActive to true\n");
|
||||
/// Note:
|
||||
/// The synthetic VAPI_NOT_ENABLED output would appear after _all_ of the trace
|
||||
/// output from the `unw_step` call. Use DEBUG instead of DEBUG-NEXT to allow
|
||||
/// for that.
|
||||
// DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=exit
|
||||
// DEBUG-NEXT: libunwind: Possible signal handler frame
|
||||
// DEBUG: libunwind: {{.*}} is within the range of VAPI address; set isKnownVapiNotActive
|
||||
/// Step from `trapper` up to `main`.
|
||||
unw_step(&cursor);
|
||||
// DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=_Z7trapperv
|
||||
// DEBUG-NOT: VAPI
|
||||
|
||||
fprintf(stderr, "Resume `main` at the call to `trapper`.\n");
|
||||
// CHECK-LABEL: Resume `main`
|
||||
if (!llu_enabled)
|
||||
fprintf(stderr,
|
||||
"libunwind: VAPI: executing return glue VAPI_NOT_ENABLED\n");
|
||||
unw_resume(&cursor);
|
||||
// DEBUG: libunwind: VAPI: executing return glue
|
||||
}
|
||||
|
||||
void trapper(void) {
|
||||
__builtin_debugtrap();
|
||||
_Exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
if (setenv("LIBUNWIND_PRINT_UNWINDING", "1", true) != 0) {
|
||||
perror("setenv");
|
||||
abort();
|
||||
}
|
||||
if (atexit(my_atexit_handler) != 0) {
|
||||
perror("atexit");
|
||||
abort();
|
||||
}
|
||||
if (signal(SIGTRAP, exit) == SIG_ERR) {
|
||||
perror("signal");
|
||||
abort();
|
||||
}
|
||||
trapper();
|
||||
_Exit(0);
|
||||
}
|
||||
Loading…
Reference in a new issue