https://github.com/hubert-reinterpretcast created https://github.com/llvm/llvm-project/pull/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 >From 9c59e1ba7e329d81c3c5025123eb5c2a24646ab2 Mon Sep 17 00:00:00 2001 From: Hubert Tong <[email protected]> Date: Tue, 14 Jul 2026 18:08:18 -0400 Subject: [PATCH 1/7] WIP: Skeleton for libunwind C API test for signal handler that is a Virtual API --- .../test/aix_vapi_signal_unwind.pass.cpp | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 libunwind/test/aix_vapi_signal_unwind.pass.cpp diff --git a/libunwind/test/aix_vapi_signal_unwind.pass.cpp b/libunwind/test/aix_vapi_signal_unwind.pass.cpp new file mode 100644 index 0000000000000..0b79f74b23076 --- /dev/null +++ b/libunwind/test/aix_vapi_signal_unwind.pass.cpp @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// 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 | FileCheck %s + +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +void my_atexit_handler(void) { + fprintf(stderr, "Retrieve a cursor to `main` by stepping up.\n"); + // CHECK: Retrieve a cursor to `main` + // TODO + fprintf(stderr, "Resume `main` at the call to `trapper.\n"); + // CHECK: Resume `main` + // TODO +} + +void trapper(void) { + __builtin_debugtrap(); + _Exit(EXIT_FAILURE); +} + +int main(void) { + if (atexit(my_atexit_handler) != 0) { + perror("atexit"); + abort(); + } + if (signal(SIGTRAP, exit) == SIG_ERR) { + perror("signal"); + abort(); + } + trapper(); + _Exit(0); +} >From a25110566e1ca525b520ced8706f17ea735cabfc Mon Sep 17 00:00:00 2001 From: Hubert Tong <[email protected]> Date: Tue, 14 Jul 2026 18:31:22 -0400 Subject: [PATCH 2/7] WIP: IBM Bob: Add API calls; enable tracing, add DEBUG-LABEL checks --- .../test/aix_vapi_signal_unwind.pass.cpp | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/libunwind/test/aix_vapi_signal_unwind.pass.cpp b/libunwind/test/aix_vapi_signal_unwind.pass.cpp index 0b79f74b23076..ec288f4434375 100644 --- a/libunwind/test/aix_vapi_signal_unwind.pass.cpp +++ b/libunwind/test/aix_vapi_signal_unwind.pass.cpp @@ -17,20 +17,36 @@ // ADDITIONAL_COMPILE_FLAGS: -fno-inline -fno-exceptions // RUN: %{build} -// RUN: %{exec} %t.exe 2>&1 | FileCheck %s +// 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> void my_atexit_handler(void) { fprintf(stderr, "Retrieve a cursor to `main` by stepping up.\n"); - // CHECK: Retrieve a cursor to `main` - // TODO - fprintf(stderr, "Resume `main` at the call to `trapper.\n"); - // CHECK: Resume `main` - // TODO + // 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); + // DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=my_atexit_handler + // Step from `exit` (signal handler, VAPI) up to `main`. + unw_step(&cursor); + // DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=exit + + fprintf(stderr, "Resume `main` at the call to `trapper`.\n"); + // CHECK-LABEL: Resume `main` + unw_resume(&cursor); + __builtin_unreachable(); + // DEBUG: libunwind: VAPI: executing return glue } void trapper(void) { @@ -39,6 +55,10 @@ void trapper(void) { } int main(void) { + if (setenv("LIBUNWIND_PRINT_UNWINDING", "1", true) != 0) { + perror("setenv"); + abort(); + } if (atexit(my_atexit_handler) != 0) { perror("atexit"); abort(); >From a3dbb34d6099239eefdafbf77a5c3237326a6868 Mon Sep 17 00:00:00 2001 From: Hubert Tong <[email protected]> Date: Tue, 14 Jul 2026 19:11:54 -0400 Subject: [PATCH 3/7] Fixup! IBM Bob: Account for `trapper` frame and C++ name mangling --- libunwind/test/aix_vapi_signal_unwind.pass.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libunwind/test/aix_vapi_signal_unwind.pass.cpp b/libunwind/test/aix_vapi_signal_unwind.pass.cpp index ec288f4434375..76b3c4839e15e 100644 --- a/libunwind/test/aix_vapi_signal_unwind.pass.cpp +++ b/libunwind/test/aix_vapi_signal_unwind.pass.cpp @@ -37,10 +37,13 @@ void my_atexit_handler(void) { unw_init_local(&cursor, &context); // Step from `my_atexit_handler` up to `exit`. unw_step(&cursor); - // DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=my_atexit_handler - // Step from `exit` (signal handler, VAPI) up to `main`. + // DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=_Z17my_atexit_handlerv + // Step from `exit` (signal handler, VAPI) up to `trapper`. unw_step(&cursor); // DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=exit + // Step from `trapper` up to `main`. + unw_step(&cursor); + // DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=_Z7trapperv fprintf(stderr, "Resume `main` at the call to `trapper`.\n"); // CHECK-LABEL: Resume `main` >From 9fb71682099774569e72eb1deebd78eeccbaaa5d Mon Sep 17 00:00:00 2001 From: Hubert Tong <[email protected]> Date: Tue, 14 Jul 2026 19:36:48 -0400 Subject: [PATCH 4/7] Add DEBUG check lines for unw_step calls --- libunwind/test/aix_vapi_signal_unwind.pass.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libunwind/test/aix_vapi_signal_unwind.pass.cpp b/libunwind/test/aix_vapi_signal_unwind.pass.cpp index 76b3c4839e15e..1a8465848da97 100644 --- a/libunwind/test/aix_vapi_signal_unwind.pass.cpp +++ b/libunwind/test/aix_vapi_signal_unwind.pass.cpp @@ -38,12 +38,16 @@ void my_atexit_handler(void) { // Step from `my_atexit_handler` up to `exit`. unw_step(&cursor); // DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=_Z17my_atexit_handlerv + // DEBUG: libunwind: the next return address={{[^ ]*}} from VAPI + // DEBUG: libunwind: The next is a signal handler frame // Step from `exit` (signal handler, VAPI) up to `trapper`. unw_step(&cursor); // DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=exit + // DEBUG-NEXT: libunwind: Possible signal handler frame // 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` >From 8fb0bd8e220ae270c9f3914c01d3b68fd3fa64be Mon Sep 17 00:00:00 2001 From: Hubert Tong <[email protected]> Date: Tue, 14 Jul 2026 19:47:42 -0400 Subject: [PATCH 5/7] IBM Bob: Add synthetic trace output when LLU is not enabled --- .../test/aix_vapi_signal_unwind.pass.cpp | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libunwind/test/aix_vapi_signal_unwind.pass.cpp b/libunwind/test/aix_vapi_signal_unwind.pass.cpp index 1a8465848da97..c40ffc15de39f 100644 --- a/libunwind/test/aix_vapi_signal_unwind.pass.cpp +++ b/libunwind/test/aix_vapi_signal_unwind.pass.cpp @@ -28,7 +28,24 @@ #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; @@ -37,6 +54,8 @@ void my_atexit_handler(void) { 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"); // DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=_Z17my_atexit_handlerv // DEBUG: libunwind: the next return address={{[^ ]*}} from VAPI // DEBUG: libunwind: The next is a signal handler frame @@ -51,6 +70,8 @@ void my_atexit_handler(void) { 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); __builtin_unreachable(); // DEBUG: libunwind: VAPI: executing return glue >From d39d71bb53c22d1897e4f6935ba5613e685f1ef5 Mon Sep 17 00:00:00 2001 From: Hubert Tong <[email protected]> Date: Tue, 14 Jul 2026 21:43:46 -0400 Subject: [PATCH 6/7] Use DEBUG-DAG to allow for synthetic trace ordering difference --- .../test/aix_vapi_signal_unwind.pass.cpp | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/libunwind/test/aix_vapi_signal_unwind.pass.cpp b/libunwind/test/aix_vapi_signal_unwind.pass.cpp index c40ffc15de39f..f9a2aa47c7d16 100644 --- a/libunwind/test/aix_vapi_signal_unwind.pass.cpp +++ b/libunwind/test/aix_vapi_signal_unwind.pass.cpp @@ -1,26 +1,26 @@ //===----------------------------------------------------------------------===// // -// 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 +/// 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. +/// 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. +/// `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 +/// REQUIRES: target={{.+}}-aix{{.*}} +/// REQUIRES: has-filecheck -// ADDITIONAL_COMPILE_FLAGS: -fno-inline -fno-exceptions +/// 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 +/// 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> @@ -28,7 +28,7 @@ #include <stdio.h> #include <stdlib.h> -// VAPI glue addresses +/// 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; @@ -52,18 +52,22 @@ void my_atexit_handler(void) { unw_cursor_t cursor; unw_getcontext(&context); unw_init_local(&cursor, &context); - // Step from `my_atexit_handler` up to `exit`. + /// 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: libunwind: the next return address={{[^ ]*}} from VAPI - // DEBUG: libunwind: The next is a signal handler frame - // Step from `exit` (signal handler, VAPI) up to `trapper`. + // 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); // DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=exit // DEBUG-NEXT: libunwind: Possible signal handler frame - // Step from `trapper` up to `main`. + /// Step from `trapper` up to `main`. unw_step(&cursor); // DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=_Z7trapperv // DEBUG-NOT: VAPI >From 33d833c727b197fcd6378baa153701d86974edaa Mon Sep 17 00:00:00 2001 From: Hubert Tong <[email protected]> Date: Tue, 14 Jul 2026 21:58:54 -0400 Subject: [PATCH 7/7] clang-format --- libunwind/test/aix_vapi_signal_unwind.pass.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libunwind/test/aix_vapi_signal_unwind.pass.cpp b/libunwind/test/aix_vapi_signal_unwind.pass.cpp index f9a2aa47c7d16..038a0d5d4359d 100644 --- a/libunwind/test/aix_vapi_signal_unwind.pass.cpp +++ b/libunwind/test/aix_vapi_signal_unwind.pass.cpp @@ -1,14 +1,15 @@ //===----------------------------------------------------------------------===// // -/// 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 +// 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. +/// `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{{.*}} @@ -55,7 +56,8 @@ void my_atexit_handler(void) { /// 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"); + 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 @@ -75,7 +77,8 @@ void my_atexit_handler(void) { 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"); + fprintf(stderr, + "libunwind: VAPI: executing return glue VAPI_NOT_ENABLED\n"); unw_resume(&cursor); __builtin_unreachable(); // DEBUG: libunwind: VAPI: executing return glue _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
