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 
&registers) {
   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

Reply via email to