https://github.com/xingxue-ibm updated https://github.com/llvm/llvm-project/pull/209280
>From 6b69974ca040d85a14a898c242f60c425b190c49 Mon Sep 17 00:00:00 2001 From: Xing Xue <[email protected]> Date: Mon, 13 Jul 2026 15:04:04 -0400 Subject: [PATCH 1/2] Update the unwinder to accommodate AIX changes required for LLU (Live Library Update). --- libcxxabi/test/aix_unwind_vapi.pass.cpp | 66 +++++++++++++++++ libunwind/src/UnwindCursor.hpp | 98 ++++++++++++++++++++++++- 2 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 libcxxabi/test/aix_unwind_vapi.pass.cpp diff --git a/libcxxabi/test/aix_unwind_vapi.pass.cpp b/libcxxabi/test/aix_unwind_vapi.pass.cpp new file mode 100644 index 0000000000000..bafb02f366ab4 --- /dev/null +++ b/libcxxabi/test/aix_unwind_vapi.pass.cpp @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Test unwinding through a frame whose return address points to the VAPI +// glue for the Live Library Update feature. + +// REQUIRES: target={{.+}}-aix{{.*}} + +#include <stdlib.h> +#include <stdio.h> + +// VAPI glue addresses +constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8B80; +constexpr uintptr_t vapi_addr_64 = 0x8e00; +constexpr uintptr_t vapi_size_64 = 0x0200; +constexpr uintptr_t vapi_glue_addr_begin = vapi_glue_addr_ext_32; // Start address in 32-bit +constexpr uintptr_t vapi_glue_addr_end = vapi_addr_64 + vapi_size_64; // End address in 64-bit + +struct FunctionDescriptor { + uintptr_t entry; + uintptr_t toc; + uintptr_t env; +}; + +struct Tracker { + const char* name; + Tracker(const char* n) : name(n) { printf("Construct %s\n", name); } + ~Tracker() { printf("Destruct %s\n", name); } +}; + +int comp(const void*, const void*) { + printf("comp()\n"); + throw 42; +} + +static void do_search() { + Tracker t("do_search-local"); + char buf[1]; + char key = 0; + printf("calling bsearch()\n"); + // bsearch uses VAPI and sets the return address to the VAPI glue when LLU + // is on. + bsearch(&key, buf, 1, 1, comp); + printf("after bsearch\n"); +} + +int main() { + FunctionDescriptor* fd = reinterpret_cast<FunctionDescriptor*>(bsearch); + + if (vapi_glue_addr_begin <= fd->entry && fd->entry < vapi_glue_addr_end) { + printf("!!! Live Library Update is enabled !!!\n"); + } else { + printf("!!! Live Library Update is NOT enabled !!!\n"); + } + + try { + do_search(); + } catch (...) { + printf("caught ...\n"); + } +} diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index ff92c9e0a844a..f59384af81b5e 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -1363,6 +1363,10 @@ class UnwindCursor : public AbstractUnwindCursor { reinterpret_cast<tbtable *>(_info.unwind_info), _registers, _isSignalFrame); } + bool isKnownVapiNotActive() const { return _isKnownVapiNotActive; } + void setIsKnownVapiNotActive(bool val) { _isKnownVapiNotActive = val; } + static pint_t getVAPILR(); + #endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) A &_addressSpace; @@ -1377,13 +1381,15 @@ class UnwindCursor : public AbstractUnwindCursor { #ifdef _LIBUNWIND_TRACE_RET_INJECT uint32_t _walkedFrames; #endif +#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + bool _isKnownVapiNotActive; +#endif }; - template <typename A, typename R> UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as) : _addressSpace(as), _registers(context), _unwindInfoMissing(false), - _isSignalFrame(false) { + _isSignalFrame(false), _isKnownVapiNotActive(false) { static_assert((check_fit<UnwindCursor<A, R>, unw_cursor_t>::does_fit), "UnwindCursor<> does not fit in unw_cursor_t"); static_assert((alignof(UnwindCursor<A, R>) <= alignof(unw_cursor_t)), @@ -1393,13 +1399,13 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as) template <typename A, typename R> UnwindCursor<A, R>::UnwindCursor(A &as, void *) - : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) { + : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false), + _isKnownVapiNotActive(false) { memset(static_cast<void *>(&_info), 0, sizeof(_info)); // FIXME // fill in _registers from thread arg } - template <typename A, typename R> bool UnwindCursor<A, R>::validReg(int regNum) { return _registers.validRegister(regNum); @@ -1469,6 +1475,30 @@ template <typename A, typename R> void UnwindCursor<A, R>::jumpto() { static constexpr size_t _EXTRA_LIBUNWIND_FRAMES_WALKED = 5 - 1; _registers.returnto(_walkedFrames + _EXTRA_LIBUNWIND_FRAMES_WALKED); #else +#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + if (isKnownVapiNotActive()) { + // If the current frame is known VAPI not active , execute the VAPI return + // glue to clear the VAPI control block. The VAPI return glue is used by + // AIX longjmp based on the VAPI active status recorded by setjmp in the + // jmp_buf, which means that the VAPI return glue can be called solely on + // the basis of the VAPI active status of the target context. + + // VAPI return glue address is the VAPI glue address - 4. +#ifdef __64BIT__ + constexpr pint_t VAPIReturnGlue = 0x8e40 - 4; +#else + constexpr pint_t VAPIReturnGlue = 0x8c40 - 4; +#endif + + _LIBUNWIND_TRACE_UNWINDING("VAPI: executing return glue %p\n", + reinterpret_cast<void *>(VAPIReturnGlue)); + register auto *registers __asm__("r30") = &_registers; + __asm__ __volatile__("bla %[retglue]" + : "+r"(registers) + : [retglue] "i"(VAPIReturnGlue)); + registers->jumpto(); + } +#endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) _registers.jumpto(); #endif } @@ -2459,6 +2489,45 @@ bool UnwindCursor<A, R>::getInfoFromTBTable(pint_t pc, R ®isters) { return true; } +// VAPI glue addresses +constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8B80; +constexpr uintptr_t vapi_addr_64 = 0x8e00; +constexpr uintptr_t vapi_size_64 = 0x0200; +constexpr uintptr_t vapi_glue_addr_begin = + vapi_glue_addr_ext_32; // Start address in 32-bit +constexpr uintptr_t vapi_glue_addr_end = + vapi_addr_64 + vapi_size_64; // End address in 64-bit + +#ifdef __64BIT__ +constexpr uintptr_t VAPI_CB_SIZE = 256; +constexpr uintptr_t TLS_POINTER_OFFSET = 30 * 1024; +constexpr uintptr_t TLSCB_BASE_SIZE = 256; + +static __inline__ __attribute__((__always_inline__)) char *tptr(void) { + char *result; + __asm__("mr %0, 13" : "=r"(result)); + return result; +} +#else // 32-bit +constexpr uintptr_t VAPI_CB_SIZE = 128; +constexpr uintptr_t TLS_POINTER_OFFSET = 31 * 1024; +constexpr uintptr_t TLSCB_BASE_SIZE = 128; + +static __inline__ __attribute__((__always_inline__)) char *tptr(void) { + char *result; + __asm__("mfspr %0, 259" : "=r"(result)); + return result; +} +#endif + +constexpr int VAPI_CB_OFFSET = + TLS_POINTER_OFFSET + VAPI_CB_SIZE + TLSCB_BASE_SIZE; + +template <typename A, typename R> +typename UnwindCursor<A, R>::pint_t UnwindCursor<A, R>::getVAPILR() { + return *reinterpret_cast<pint_t *>(tptr() - VAPI_CB_OFFSET + 8); +} + // Step back up the stack following the frame back link. template <typename A, typename R> int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable, @@ -2579,6 +2648,17 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable, } else { // Otherwise, use the LR value in the stack link area. returnAddress = reinterpret_cast<pint_t *>(lastStack)[2]; + + if (vapi_glue_addr_begin <= returnAddress && + returnAddress < vapi_glue_addr_end) { + _LIBUNWIND_TRACE_UNWINDING( + "The return address=%p is within the range of VAPI address, ", + reinterpret_cast<void *>(returnAddress)); + setIsKnownVapiNotActive(true); + returnAddress = getVAPILR(); + _LIBUNWIND_TRACE_UNWINDING("return address=%p from VAPI\n", + reinterpret_cast<void *>(returnAddress)); + } } // Reset LR in the current context. @@ -2709,6 +2789,16 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable, // Return address is the address after call site instruction. pint_t nextReturnAddress = reinterpret_cast<pint_t *>(nextStack)[2]; + if (vapi_glue_addr_begin <= nextReturnAddress && + nextReturnAddress < vapi_glue_addr_end) { + _LIBUNWIND_TRACE_UNWINDING( + "The next return address=%p is within the range of VAPI address, ", + reinterpret_cast<void *>(nextReturnAddress)); + nextReturnAddress = getVAPILR(); + _LIBUNWIND_TRACE_UNWINDING("the next return address=%p from VAPI\n", + reinterpret_cast<void *>(nextReturnAddress)); + } + if (nextReturnAddress > 0x01 && nextReturnAddress < 0x10000) { _LIBUNWIND_TRACE_UNWINDING("The next is a signal handler frame: " "nextStack=%p, next return address=%p\n", >From 578ab2193990e1954f302654d3f17fa40e639f0e Mon Sep 17 00:00:00 2001 From: Xing Xue <[email protected]> Date: Mon, 13 Jul 2026 15:47:23 -0400 Subject: [PATCH 2/2] Guard _isKnownVapiNotActive(false) initializers with #if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND). --- libunwind/src/UnwindCursor.hpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index f59384af81b5e..7b400964defc8 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -1389,7 +1389,11 @@ class UnwindCursor : public AbstractUnwindCursor { template <typename A, typename R> UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as) : _addressSpace(as), _registers(context), _unwindInfoMissing(false), - _isSignalFrame(false), _isKnownVapiNotActive(false) { + _isSignalFrame(false) +#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + , _isKnownVapiNotActive(false) +#endif + { static_assert((check_fit<UnwindCursor<A, R>, unw_cursor_t>::does_fit), "UnwindCursor<> does not fit in unw_cursor_t"); static_assert((alignof(UnwindCursor<A, R>) <= alignof(unw_cursor_t)), @@ -1399,8 +1403,11 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as) template <typename A, typename R> UnwindCursor<A, R>::UnwindCursor(A &as, void *) - : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false), - _isKnownVapiNotActive(false) { + : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) +#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + , _isKnownVapiNotActive(false) +#endif + { memset(static_cast<void *>(&_info), 0, sizeof(_info)); // FIXME // fill in _registers from thread arg _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
