| //===----------------------------------------------------------------------===// | |
| // | |
| // 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); | |
| } |