Skip to content

Commit 77b1291

Browse files
hubert-reinterpretcasttru
authored andcommitted
[libunwind][test][AIX] Add C API test for unwinding from AIX VAPI (non-signal-handler case) (llvm#209306)
Test detection, during stepping, of the backchain mutation introduced by a VAPI call (see llvm#209280). Further, test resumption of contexts using cursors obtained while a VAPI is active on the thread. Testing is done via FileCheck inspection of trace output enabled by `LIBUNWIND_PRINT_UNWINDING=1`. When Live Library Update is not enabled, synthetic trace output is generated by the test program itself. --------- Assisted-by: IBM Bob (cherry picked from commit 861efe0)
1 parent 4977a9a commit 77b1291

3 files changed

Lines changed: 170 additions & 0 deletions

File tree

libunwind/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ endforeach()
2727

2828
pythonize_bool(HAVE_GETAUXVAL)
2929
pythonize_bool(HAVE_ELF_AUX_INFO)
30+
pythonize_bool(LIBUNWIND_ENABLE_ASSERTIONS)
3031
pythonize_bool(LIBUNWIND_ENABLE_CET)
3132
pythonize_bool(LIBUNWIND_ENABLE_GCS)
3233
pythonize_bool(LIBUNWIND_ENABLE_THREADS)
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// REQUIRES: target={{.+}}-aix{{.*}}
10+
// REQUIRES: has-filecheck
11+
12+
// ADDITIONAL_COMPILE_FLAGS: -fno-inline -fno-exceptions
13+
14+
// RUN: %{build}
15+
// RUN: %{exec} %t.exe 2>&1 \
16+
// RUN: | FileCheck --check-prefix=CHECK \
17+
// RUN: %if libunwind-assertions-enabled %{ --check-prefix=DEBUG %} \
18+
// RUN: %s
19+
20+
// Tests use of the libunwind C API to step up from a context where the VAPI is
21+
// active and to resume contexts where
22+
// - the VAPI is active (and thus VAPI return glue is not called) and
23+
// - where the VAPI is not active (and thus VAPI return glue _is_ called).
24+
//
25+
// In the latter case, which applies not just to the caller of the Virtual API
26+
// but also to its ancestors, the return glue should always be called (i.e.,
27+
// each time, without regard for whether the VAPI is currently active).
28+
29+
#include <assert.h>
30+
#include <libunwind.h>
31+
#include <stdio.h>
32+
#include <stdlib.h>
33+
34+
#ifdef __64BIT__
35+
#define R3 UNW_PPC64_R3
36+
#else
37+
#define R3 UNW_PPC_R3
38+
#endif
39+
40+
extern "C" void *returns_twice_bsearch
41+
[[gnu::returns_twice]] (const void *, const void *, size_t, size_t,
42+
int (*)(const void *,
43+
const void *)) __asm__("bsearch");
44+
45+
static bool llu_enabled;
46+
static unw_cursor_t bsearch_cursor;
47+
static unw_cursor_t bsearch_caller_cursor;
48+
static unw_cursor_t main_cursor;
49+
50+
extern "C" int cmp(const void *pa, const void *pb) {
51+
(void)pa;
52+
(void)pb;
53+
54+
fprintf(
55+
stderr,
56+
"Populate global cursors for `bsearch`, `bsearch_caller`, and `main`.\n");
57+
// CHECK-LABEL: Populate global cursors
58+
unw_context_t context;
59+
unw_cursor_t cursor;
60+
unw_getcontext(&context);
61+
unw_init_local(&cursor, &context);
62+
// Step from `cmp` up to `bsearch`.
63+
unw_step(&cursor);
64+
if (!llu_enabled)
65+
fprintf(stderr,
66+
"libunwind: the next return address=VAPI_NOT_ENABLED from VAPI\n");
67+
// DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=cmp
68+
// DEBUG: libunwind: the next return address=[[VAPI_RA:[^ ]*]] from VAPI
69+
bsearch_cursor = cursor;
70+
// Step from `bsearch` up to `bsearch_caller`.
71+
unw_step(&cursor);
72+
if (!llu_enabled)
73+
fprintf(stderr, "libunwind: return address=VAPI_NOT_ENABLED from VAPI\n");
74+
// DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=bsearch
75+
// DEBUG: libunwind: return address=[[VAPI_RA]] from VAPI
76+
bsearch_caller_cursor = cursor;
77+
// Step from `bsearch_caller` up to `main`.
78+
unw_step(&cursor);
79+
// DEBUG-LABEL: libunwind: stepWithTBTable: Look up traceback table of func=_Z14bsearch_callerv
80+
// DEBUG-NOT: VAPI
81+
main_cursor = cursor;
82+
83+
// Test resuming context where VAPI is active.
84+
fprintf(stderr,
85+
"Return to `bsearch` with r3 set to 0 using the global cursor.\n");
86+
unw_set_reg(&bsearch_cursor, R3, (unw_word_t)0);
87+
unw_resume(&bsearch_cursor);
88+
__builtin_unreachable();
89+
// CHECK-LABEL: Return to `bsearch`
90+
// DEBUG-NOT: VAPI: executing return glue
91+
}
92+
93+
char bsearch_caller_ret;
94+
void *bsearch_caller(void) {
95+
volatile int state = 0;
96+
97+
char c;
98+
char buf[3];
99+
#pragma clang diagnostic push
100+
#pragma clang diagnostic ignored "-Wuninitialized-const-pointer"
101+
void *result = returns_twice_bsearch(&c, buf, 1, 1, cmp);
102+
#pragma clang diagnostic pop
103+
assert(result == &buf[state]);
104+
105+
// Test resuming context where VAPI is not active. The VAPI return glue should
106+
// be used each time without regard for whether the VAPI is currently active.
107+
108+
// `state` is volatile to observe changes in the value between the multiple
109+
// returns in `setjmp`/`longjmp`-like usage. We need to spell out the
110+
// `volatile` accesses very explicitly thanks to changes in the C++ standard.
111+
if ((state = state + 1, state) < 3) {
112+
fprintf(stderr,
113+
"Return to `bsearch_caller` at the invocation of "
114+
"`returns_twice_bsearch` (really `bsearch`) with r3 set to "
115+
"&buf[%d] using the global cursor.\n",
116+
state);
117+
if (!llu_enabled)
118+
fprintf(stderr,
119+
"libunwind: VAPI: executing return glue VAPI_NOT_ENABLED\n");
120+
unw_set_reg(&bsearch_caller_cursor, R3, (unw_word_t)&buf[state]);
121+
unw_resume(&bsearch_caller_cursor);
122+
__builtin_unreachable();
123+
// CHECK-LABEL: Return to `bsearch_caller` {{.*}}&buf[1]
124+
// DEBUG: libunwind: VAPI: executing return glue
125+
// CHECK-LABEL: Return to `bsearch_caller` {{.*}}&buf[2]
126+
// DEBUG: libunwind: VAPI: executing return glue
127+
}
128+
129+
// Test resuming context where VAPI is not active, one frame up from the VAPI
130+
// caller.
131+
fprintf(stderr, "Return to `main` at the invocation of `bsearch_caller` with "
132+
"r3 set to `&bsearch_caller_ret` using the global cursor.\n");
133+
if (!llu_enabled)
134+
fprintf(stderr,
135+
"libunwind: VAPI: executing return glue VAPI_NOT_ENABLED\n");
136+
unw_set_reg(&main_cursor, R3, (unw_word_t)&bsearch_caller_ret);
137+
unw_resume(&main_cursor);
138+
__builtin_unreachable();
139+
// CHECK-LABEL: Return to `main`
140+
// DEBUG: libunwind: VAPI: executing return glue
141+
}
142+
143+
// VAPI glue addresses
144+
constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8b80;
145+
constexpr uintptr_t vapi_addr_64 = 0x8e00;
146+
constexpr size_t vapi_size_64 = 0x0200;
147+
constexpr uintptr_t vapi_glue_addr_begin = vapi_glue_addr_ext_32;
148+
constexpr uintptr_t vapi_glue_addr_end = vapi_addr_64 + vapi_size_64;
149+
150+
struct FunctionDescriptor {
151+
uintptr_t entry;
152+
uintptr_t toc;
153+
uintptr_t env;
154+
};
155+
156+
int main(void) {
157+
if (setenv("LIBUNWIND_PRINT_UNWINDING", "1", true) != 0) {
158+
perror("setenv");
159+
abort();
160+
}
161+
162+
FunctionDescriptor *fd = reinterpret_cast<FunctionDescriptor *>(bsearch);
163+
llu_enabled =
164+
vapi_glue_addr_begin <= fd->entry && fd->entry < vapi_glue_addr_end;
165+
166+
assert(bsearch_caller() == &bsearch_caller_ret);
167+
}

libunwind/test/configs/cmake-bridge.cfg.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ config.test_exec_root = os.path.join('@LIBUNWIND_BINARY_DIR@', 'test')
2727
# Add a few features that are common to all the configurations
2828
if @LIBUNWIND_USES_ARM_EHABI@:
2929
config.available_features.add('libunwind-arm-ehabi')
30+
if @LIBUNWIND_ENABLE_ASSERTIONS@:
31+
config.available_features.add('libunwind-assertions-enabled')
3032
if not @LIBUNWIND_ENABLE_THREADS@:
3133
config.available_features.add('libunwind-no-threads')
3234

0 commit comments

Comments
 (0)