[llvm-branch-commits] [libunwind] 62d4ad9 - [libunwind][AIX] Handle VAPI-based return addresses in stack unwinding for LLU (#209280)
Tobias Hieta via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Jul 22 23:07:05 PDT 2026
Author: Xing Xue
Date: 2026-07-23T08:06:55+02:00
New Revision: 62d4ad9d04ce02a59edd12170418b93ea6334d7a
URL: https://github.com/llvm/llvm-project/commit/62d4ad9d04ce02a59edd12170418b93ea6334d7a
DIFF: https://github.com/llvm/llvm-project/commit/62d4ad9d04ce02a59edd12170418b93ea6334d7a.diff
LOG: [libunwind][AIX] Handle VAPI-based return addresses in stack unwinding for LLU (#209280)
In AIX's implementation of LLU (Live Library Update), the caller of a
Virtual API (VAPI) interface instead calls VAPI glue and the
implementation for a VAPI function is entered from the VAPI glue. If a
VAPI is not already active on the thread, the VAPI calls the
implementation of the VAPI function with the link register (LR) value
set to a return address in the VAPI glue. In this case, the LR (return
address) value on entry to the VAPI glue is saved in the VAPI control
block. This PR checks whether the return address in a stack frame falls
within the VAPI address range. If it does, the unwinder retrieves the LR
value from the VAPI control block and uses it as the return address
during stack unwinding. In addition, before transferring control to a
landing pad, this PR executes the VAPI return glue to clear the VAPI
control block if the VAPI glue was used.
---------
Co-authored-by: Hubert Tong <hubert.reinterpretcast at gmail.com>
(cherry picked from commit 8aa808457c6aeba25ec0e17139c3fcab59923c3c)
Added:
Modified:
libunwind/src/UnwindCursor.hpp
Removed:
################################################################################
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index ff92c9e0a844a..537dbd83f4f74 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,23 @@ class UnwindCursor : public AbstractUnwindCursor {
#ifdef _LIBUNWIND_TRACE_RET_INJECT
uint32_t _walkedFrames;
#endif
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+ // TODO: this will need to be recorded in the unw_context_t by unw_getcontext
+ // to support cases where the cursor is retrieved prior to invocation of the
+ // Virtual API.
+ 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)
+#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)),
@@ -1393,13 +1407,17 @@ 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)
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+ ,
+ _isKnownVapiNotActive(false)
+#endif
+{
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 +1487,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 +2501,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 size_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 size_t VAPI_CB_SIZE = 256;
+constexpr ptr
diff _t TLS_POINTER_OFFSET = 30 * 1024;
+constexpr size_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 size_t VAPI_CB_SIZE = 128;
+constexpr ptr
diff _t TLS_POINTER_OFFSET = 31 * 1024;
+constexpr size_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 ptr
diff _t 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,
@@ -2511,6 +2592,16 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
_LIBUNWIND_TRACE_UNWINDING("Possible signal handler frame: lastStack=%p",
reinterpret_cast<void *>(lastStack));
+ pint_t returnAddressInStack = reinterpret_cast<pint_t *>(lastStack)[2];
+ if (vapi_glue_addr_begin <= returnAddressInStack &&
+ returnAddressInStack < vapi_glue_addr_end) {
+ _LIBUNWIND_TRACE_UNWINDING(
+ "The return address in stack %p is within the range of VAPI address;"
+ " set isKnownVapiNotActive to true\n",
+ reinterpret_cast<void *>(returnAddressInStack));
+ setIsKnownVapiNotActive(true);
+ }
+
sigcontext *sigContext = reinterpret_cast<sigcontext *>(
reinterpret_cast<char *>(lastStack) + STKMINALIGN);
returnAddress = sigContext->sc_jmpbuf.jmp_context.iar;
@@ -2579,6 +2670,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 +2811,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",
More information about the llvm-branch-commits
mailing list