From: Ulrich Weigand Date: Wed, 4 May 2022 08:43:11 +0000 (+0200) Subject: [libunwind][SystemZ] Unwind out of signal handlers X-Git-Tag: upstream/15.0.7~8679 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=71672375fe91d602699ae2a6d6a88e910ff91b5c;p=platform%2Fupstream%2Fllvm.git [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 --- 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 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::stepWithTBTable(pint_t pc, tbtable *TBTable, template void UnwindCursor::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::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::stepThroughSigReturn(Registers_arm64 &) { } #endif // defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) +#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_S390X) +template +bool UnwindCursor::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(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 +int UnwindCursor::stepThroughSigReturn(Registers_s390x &) { + // Determine current SP. + const pint_t sp = static_cast(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(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(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(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 int UnwindCursor::step() { // Bottom of stack is defined is when unwind info cannot be found. @@ -2661,7 +2765,7 @@ int UnwindCursor::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 diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp index 4f2e925..2ff50ab 100644 --- a/libunwind/test/signal_unwind.pass.cpp +++ b/libunwind/test/signal_unwind.pass.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // Ensure that the unwinder can cope with the signal handler. -// REQUIRES: linux && (target={{aarch64-.+}} || target={{x86_64-.+}}) +// REQUIRES: linux && (target={{aarch64-.+}} || target={{s390x-.+}} || target={{x86_64-.+}}) // TODO: Figure out why this fails with Memory Sanitizer. // XFAIL: msan diff --git a/libunwind/test/unwind_leaffunction.pass.cpp b/libunwind/test/unwind_leaffunction.pass.cpp index a112d75..f363dfe 100644 --- a/libunwind/test/unwind_leaffunction.pass.cpp +++ b/libunwind/test/unwind_leaffunction.pass.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // Ensure that leaf function can be unwund. -// REQUIRES: linux && (target={{aarch64-.+}} || target={{x86_64-.+}}) +// REQUIRES: linux && (target={{aarch64-.+}} || target={{s390x-.+}} || target={{x86_64-.+}}) // TODO: Figure out why this fails with Memory Sanitizer. // XFAIL: msan