aboutsummaryrefslogtreecommitdiff
path: root/libunwind/src
diff options
context:
space:
mode:
authorUlrich Weigand <ulrich.weigand@de.ibm.com>2022-05-04 10:43:11 +0200
committerUlrich Weigand <ulrich.weigand@de.ibm.com>2022-05-04 10:43:11 +0200
commit71672375fe91d602699ae2a6d6a88e910ff91b5c (patch)
tree0b76298f28133c99e741feeb1bf364e4c6bfdeb6 /libunwind/src
parentf1b9245199f3457a4d06d32d1bc6e44573c166e3 (diff)
downloadllvm-71672375fe91d602699ae2a6d6a88e910ff91b5c.zip
llvm-71672375fe91d602699ae2a6d6a88e910ff91b5c.tar.gz
llvm-71672375fe91d602699ae2a6d6a88e910ff91b5c.tar.bz2
[libunwind][SystemZ] Unwind out of signal handlers
Unwinding out of signal handlers currently does not work since the sigreturn trampoline is not annotated with CFI data. Fix this by detecting the sigreturn trampoline during unwinding and providing appropriate unwind data manually. This follows closely the approach used by existing code for the AArch64 target. Reviewed by: MaskRay Differential Revision: https://reviews.llvm.org/D124765
Diffstat (limited to 'libunwind/src')
-rw-r--r--libunwind/src/UnwindCursor.hpp114
1 files changed, 109 insertions, 5 deletions
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 655e41d..7e56eff 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -953,7 +953,7 @@ private:
}
#endif
-#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64)
+#if defined(_LIBUNWIND_TARGET_LINUX) && (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_S390X))
bool setInfoForSigReturn() {
R dummy;
return setInfoForSigReturn(dummy);
@@ -962,8 +962,14 @@ private:
R dummy;
return stepThroughSigReturn(dummy);
}
+ #if defined(_LIBUNWIND_TARGET_AARCH64)
bool setInfoForSigReturn(Registers_arm64 &);
int stepThroughSigReturn(Registers_arm64 &);
+ #endif
+ #if defined(_LIBUNWIND_TARGET_S390X)
+ bool setInfoForSigReturn(Registers_s390x &);
+ int stepThroughSigReturn(Registers_s390x &);
+ #endif
template <typename Registers> bool setInfoForSigReturn(Registers &) {
return false;
}
@@ -1258,7 +1264,7 @@ private:
unw_proc_info_t _info;
bool _unwindInfoMissing;
bool _isSignalFrame;
-#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64)
+#if defined(_LIBUNWIND_TARGET_LINUX) && (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_S390X))
bool _isSigReturn = false;
#endif
};
@@ -2465,7 +2471,7 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
template <typename A, typename R>
void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {
-#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64)
+#if defined(_LIBUNWIND_TARGET_LINUX) && (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_S390X))
_isSigReturn = false;
#endif
@@ -2580,7 +2586,7 @@ void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {
}
#endif // #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
-#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64)
+#if defined(_LIBUNWIND_TARGET_LINUX) && (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_S390X))
if (setInfoForSigReturn())
return;
#endif
@@ -2653,6 +2659,104 @@ int UnwindCursor<A, R>::stepThroughSigReturn(Registers_arm64 &) {
}
#endif // defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64)
+#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_S390X)
+template <typename A, typename R>
+bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_s390x &) {
+ // Look for the sigreturn trampoline. The trampoline's body is a
+ // specific instruction (see below). Typically the trampoline comes from the
+ // vDSO (i.e. the __kernel_[rt_]sigreturn function). A libc might provide its
+ // own restorer function, though, or user-mode QEMU might write a trampoline
+ // onto the stack.
+ const pint_t pc = static_cast<pint_t>(this->getReg(UNW_REG_IP));
+ const uint16_t inst = _addressSpace.get16(pc);
+ if (inst == 0x0a77 || inst == 0x0aad) {
+ _info = {};
+ _info.start_ip = pc;
+ _info.end_ip = pc + 2;
+ _isSigReturn = true;
+ return true;
+ }
+ return false;
+}
+
+template <typename A, typename R>
+int UnwindCursor<A, R>::stepThroughSigReturn(Registers_s390x &) {
+ // Determine current SP.
+ const pint_t sp = static_cast<pint_t>(this->getReg(UNW_REG_SP));
+ // According to the s390x ABI, the CFA is at (incoming) SP + 160.
+ const pint_t cfa = sp + 160;
+
+ // Determine current PC and instruction there (this must be either
+ // a "svc __NR_sigreturn" or "svc __NR_rt_sigreturn").
+ const pint_t pc = static_cast<pint_t>(this->getReg(UNW_REG_IP));
+ const uint16_t inst = _addressSpace.get16(pc);
+
+ // Find the addresses of the signo and sigcontext in the frame.
+ pint_t pSigctx = 0;
+ pint_t pSigno = 0;
+
+ // "svc __NR_sigreturn" uses a non-RT signal trampoline frame.
+ if (inst == 0x0a77) {
+ // Layout of a non-RT signal trampoline frame, starting at the CFA:
+ // - 8-byte signal mask
+ // - 8-byte pointer to sigcontext, followed by signo
+ // - 4-byte signo
+ pSigctx = _addressSpace.get64(cfa + 8);
+ pSigno = pSigctx + 344;
+ }
+
+ // "svc __NR_rt_sigreturn" uses a RT signal trampoline frame.
+ if (inst == 0x0aad) {
+ // Layout of a RT signal trampoline frame, starting at the CFA:
+ // - 8-byte retcode (+ alignment)
+ // - 128-byte siginfo struct (starts with signo)
+ // - ucontext struct:
+ // - 8-byte long (uc_flags)
+ // - 8-byte pointer (uc_link)
+ // - 24-byte stack_t
+ // - 8 bytes of padding because sigcontext has 16-byte alignment
+ // - sigcontext/mcontext_t
+ pSigctx = cfa + 8 + 128 + 8 + 8 + 24 + 8;
+ pSigno = cfa + 8;
+ }
+
+ assert(pSigctx != 0);
+ assert(pSigno != 0);
+
+ // Offsets from sigcontext to each register.
+ const pint_t kOffsetPc = 8;
+ const pint_t kOffsetGprs = 16;
+ const pint_t kOffsetFprs = 216;
+
+ // Restore all registers.
+ for (int i = 0; i < 16; ++i) {
+ uint64_t value = _addressSpace.get64(pSigctx + kOffsetGprs +
+ static_cast<pint_t>(i * 8));
+ _registers.setRegister(UNW_S390X_R0 + i, value);
+ }
+ for (int i = 0; i < 16; ++i) {
+ static const int fpr[16] = {
+ UNW_S390X_F0, UNW_S390X_F1, UNW_S390X_F2, UNW_S390X_F3,
+ UNW_S390X_F4, UNW_S390X_F5, UNW_S390X_F6, UNW_S390X_F7,
+ UNW_S390X_F8, UNW_S390X_F9, UNW_S390X_F10, UNW_S390X_F11,
+ UNW_S390X_F12, UNW_S390X_F13, UNW_S390X_F14, UNW_S390X_F15
+ };
+ double value = _addressSpace.getDouble(pSigctx + kOffsetFprs +
+ static_cast<pint_t>(i * 8));
+ _registers.setFloatRegister(fpr[i], value);
+ }
+ _registers.setIP(_addressSpace.get64(pSigctx + kOffsetPc));
+
+ // SIGILL, SIGFPE and SIGTRAP are delivered with psw_addr
+ // after the faulting instruction rather than before it.
+ // Do not set _isSignalFrame in that case.
+ uint32_t signo = _addressSpace.get32(pSigno);
+ _isSignalFrame = (signo != 4 && signo != 5 && signo != 8);
+
+ return UNW_STEP_SUCCESS;
+}
+#endif // defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_S390X)
+
template <typename A, typename R>
int UnwindCursor<A, R>::step() {
// Bottom of stack is defined is when unwind info cannot be found.
@@ -2661,7 +2765,7 @@ int UnwindCursor<A, R>::step() {
// Use unwinding info to modify register set as if function returned.
int result;
-#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64)
+#if defined(_LIBUNWIND_TARGET_LINUX) && (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_S390X))
if (_isSigReturn) {
result = this->stepThroughSigReturn();
} else