}
#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);
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;
}
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
};
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
}
#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
}
#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.
// 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