#include <assert.h>
#include <libunwind.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __64BIT__
#define R3 UNW_PPC64_R3
#else
#define R3 UNW_PPC_R3
#endif
extern "C" void *returns_twice_bsearch
[[gnu::returns_twice]] (const void *, const void *, size_t, size_t,
int (*)(const void *,
const void *)) __asm__("bsearch");
static bool llu_enabled;
static unw_cursor_t bsearch_cursor;
static unw_cursor_t bsearch_caller_cursor;
static unw_cursor_t main_cursor;
extern "C" int cmp(const void *pa, const void *pb) {
(void)pa;
(void)pb;
fprintf(
stderr,
"Populate global cursors for `bsearch`, `bsearch_caller`, and `main`.\n");
unw_context_t context;
unw_cursor_t cursor;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
unw_step(&cursor);
if (!llu_enabled)
fprintf(stderr,
"libunwind: the next return address=VAPI_NOT_ENABLED from VAPI\n");
bsearch_cursor = cursor;
unw_step(&cursor);
if (!llu_enabled)
fprintf(stderr, "libunwind: return address=VAPI_NOT_ENABLED from VAPI\n");
bsearch_caller_cursor = cursor;
unw_step(&cursor);
main_cursor = cursor;
fprintf(stderr,
"Return to `bsearch` with r3 set to 0 using the global cursor.\n");
unw_set_reg(&bsearch_cursor, R3, (unw_word_t)0);
unw_resume(&bsearch_cursor);
__builtin_unreachable();
}
char bsearch_caller_ret;
void *bsearch_caller(void) {
volatile int state = 0;
char c;
char buf[3];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wuninitialized-const-pointer"
void *result = returns_twice_bsearch(&c, buf, 1, 1, cmp);
#pragma clang diagnostic pop
assert(result == &buf[state]);
if ((state = state + 1, state) < 3) {
fprintf(stderr,
"Return to `bsearch_caller` at the invocation of "
"`returns_twice_bsearch` (really `bsearch`) with r3 set to "
"&buf[%d] using the global cursor.\n",
state);
if (!llu_enabled)
fprintf(stderr,
"libunwind: VAPI: executing return glue VAPI_NOT_ENABLED\n");
unw_set_reg(&bsearch_caller_cursor, R3, (unw_word_t)&buf[state]);
unw_resume(&bsearch_caller_cursor);
__builtin_unreachable();
}
fprintf(stderr, "Return to `main` at the invocation of `bsearch_caller` with "
"r3 set to `&bsearch_caller_ret` using the global cursor.\n");
if (!llu_enabled)
fprintf(stderr,
"libunwind: VAPI: executing return glue VAPI_NOT_ENABLED\n");
unw_set_reg(&main_cursor, R3, (unw_word_t)&bsearch_caller_ret);
unw_resume(&main_cursor);
__builtin_unreachable();
}
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;
};
int main(void) {
if (setenv("LIBUNWIND_PRINT_UNWINDING", "1", true) != 0) {
perror("setenv");
abort();
}
FunctionDescriptor *fd = reinterpret_cast<FunctionDescriptor *>(bsearch);
llu_enabled =
vapi_glue_addr_begin <= fd->entry && fd->entry < vapi_glue_addr_end;
assert(bsearch_caller() == &bsearch_caller_ret);
}