llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-libunwind Author: Minsoo Choo (mchoo7) <details> <summary>Changes</summary> Retried from https://reviews.llvm.org/D155066. Recognize signal trampolines using KERN_PROC_SIGTRAMP when unwind metadata is unavailable. Restore the interrupted PC, LR, SP, general registers, and valid floating-point registers without increasing the cursor size. Add coverage for unwinding out of an interrupted leaf function on both regular and alternate signal stacks. Assisted-by: GPT --- Full diff: https://github.com/llvm/llvm-project/pull/235065.diff 4 Files Affected: - (modified) libunwind/include/__libunwind_config.h (+3) - (modified) libunwind/src/UnwindCursor.hpp (+76-1) - (added) libunwind/test/aarch64_freebsd_signal_unwind.pass.cpp (+103) - (modified) libunwind/test/aarch64_za_unwind.pass.cpp (+2) ``````````diff diff --git a/libunwind/include/__libunwind_config.h b/libunwind/include/__libunwind_config.h index 0c7aed13375a14..60ab1bdc2cf757 100644 --- a/libunwind/include/__libunwind_config.h +++ b/libunwind/include/__libunwind_config.h @@ -36,6 +36,9 @@ # if defined(__linux__) # define _LIBUNWIND_TARGET_LINUX 1 # endif +# if defined(__FreeBSD__) +# define _LIBUNWIND_TARGET_FREEBSD 1 +# endif # if defined(__HAIKU__) # define _LIBUNWIND_TARGET_HAIKU 1 # endif diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index f307190addadac..dfd5b2b19e1b48 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -41,6 +41,17 @@ #define _LIBUNWIND_CHECK_LINUX_SIGRETURN 1 #endif +#if defined(_LIBUNWIND_TARGET_FREEBSD) && defined(_LIBUNWIND_TARGET_AARCH64) +#include <errno.h> +#include <machine/frame.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/ucontext.h> +#include <sys/user.h> +#include <unistd.h> +#define _LIBUNWIND_CHECK_FREEBSD_SIGRETURN 1 +#endif + #if defined(_LIBUNWIND_TARGET_HAIKU) && \ (defined(_LIBUNWIND_TARGET_I386) || defined(_LIBUNWIND_TARGET_X86_64)) #include <OS.h> @@ -1055,7 +1066,8 @@ class UnwindCursor : public AbstractUnwindCursor { template <typename Registers> int stepThroughSigReturn(Registers &) { return UNW_STEP_END; } -#elif defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) +#elif defined(_LIBUNWIND_CHECK_FREEBSD_SIGRETURN) || \ + defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) bool setInfoForSigReturn(); int stepThroughSigReturn(); #endif @@ -1377,6 +1389,7 @@ class UnwindCursor : public AbstractUnwindCursor { bool _unwindInfoMissing; bool _isSignalFrame; #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) || \ + defined(_LIBUNWIND_CHECK_FREEBSD_SIGRETURN) || \ defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) bool _isSigReturn = false; #endif @@ -2890,6 +2903,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_CHECK_LINUX_SIGRETURN) || \ + defined(_LIBUNWIND_CHECK_FREEBSD_SIGRETURN) || \ defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) _isSigReturn = false; #endif @@ -3023,6 +3037,7 @@ void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) { #endif // #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) || \ + defined(_LIBUNWIND_CHECK_FREEBSD_SIGRETURN) || \ defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) if (setInfoForSigReturn()) return; @@ -3329,6 +3344,65 @@ int UnwindCursor<A, R>::stepThroughSigReturn(Registers_s390x &) { #endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && // defined(_LIBUNWIND_TARGET_S390X) +#if defined(_LIBUNWIND_CHECK_FREEBSD_SIGRETURN) +template <typename A, typename R> +bool UnwindCursor<A, R>::setInfoForSigReturn() { + // Ask the kernel rather than reading instructions at a possibly invalid PC. + // This is only used when no unwind information was found. Keep the result + // local to avoid shared mutable state when unwinding from a signal handler. + struct kinfo_sigtramp kst = {}; + size_t len = sizeof(kst); + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_SIGTRAMP, getpid()}; + const int savedErrno = errno; + const int result = sysctl(mib, 4, &kst, &len, nullptr, 0); + errno = savedErrno; + if (result != 0) + return false; + + const pint_t pc = static_cast<pint_t>(this->getReg(UNW_REG_IP)); + const pint_t start = reinterpret_cast<pint_t>(kst.ksigtramp_start); + const pint_t end = reinterpret_cast<pint_t>(kst.ksigtramp_end); + if (pc < start || pc >= end) + return false; + + _info = {}; + _info.start_ip = start; + _info.end_ip = end; + _isSigReturn = true; + return true; +} + +#if defined(_LIBUNWIND_TARGET_AARCH64) +template <typename A, typename R> +int UnwindCursor<A, R>::stepThroughSigReturn() { + // The trampoline leaves SP pointing at the sigframe installed by sendsig(). + const pint_t sigctx = _registers.getSP() + offsetof(struct sigframe, sf_uc) + + offsetof(ucontext_t, uc_mcontext); + const pint_t gprs = sigctx + offsetof(mcontext_t, mc_gpregs); + for (int i = 0; i < 30; ++i) + _registers.setRegister( + UNW_AARCH64_X0 + i, + _addressSpace.get64(gprs + offsetof(struct gpregs, gp_x) + i * 8)); + _registers.setRegister( + UNW_AARCH64_LR, + _addressSpace.get64(gprs + offsetof(struct gpregs, gp_lr))); + _registers.setSP(_addressSpace.get64(gprs + offsetof(struct gpregs, gp_sp))); + _registers.setIP(_addressSpace.get64(gprs + offsetof(struct gpregs, gp_elr))); + + if (_addressSpace.get32(sigctx + offsetof(mcontext_t, mc_flags)) & + _MC_FP_VALID) { + const pint_t fprs = sigctx + offsetof(mcontext_t, mc_fpregs) + + offsetof(struct fpregs, fp_q); + for (int i = 0; i < 32; ++i) + _registers.setFloatRegister(UNW_AARCH64_V0 + i, + _addressSpace.getDouble(fprs + i * 16)); + } + _isSignalFrame = true; + return UNW_STEP_SUCCESS; +} +#endif // defined(_LIBUNWIND_TARGET_AARCH64) +#endif // defined(_LIBUNWIND_CHECK_FREEBSD_SIGRETURN) + #if defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) template <typename A, typename R> bool UnwindCursor<A, R>::setInfoForSigReturn() { @@ -3428,6 +3502,7 @@ template <typename A, typename R> int UnwindCursor<A, R>::step(bool stage2) { // Use unwinding info to modify register set as if function returned. int result; #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) || \ + defined(_LIBUNWIND_CHECK_FREEBSD_SIGRETURN) || \ defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) if (_isSigReturn) { result = this->stepThroughSigReturn(); diff --git a/libunwind/test/aarch64_freebsd_signal_unwind.pass.cpp b/libunwind/test/aarch64_freebsd_signal_unwind.pass.cpp new file mode 100644 index 00000000000000..44be6c507dedc1 --- /dev/null +++ b/libunwind/test/aarch64_freebsd_signal_unwind.pass.cpp @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: target={{aarch64-.*-freebsd.*}} +// UNSUPPORTED: msan + +// Unwind through the kernel's signal trampoline, including on an alternate +// stack. In particular, restore the interrupted PC rather than the saved LR: +// the latter would skip the interrupted leaf function altogether. + +#undef NDEBUG +#include "support/func_bounds.h" +#include <assert.h> +#include <libunwind.h> +#include <signal.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ucontext.h> + +FUNC_BOUNDS_DECL(main_func); +static volatile sig_atomic_t handled; + +static void check_reg(unw_cursor_t *cursor, int reg, unw_word_t expected) { + unw_word_t value; + assert(unw_get_reg(cursor, reg, &value) == 0); + assert(value == expected); +} + +static void handler(int, siginfo_t *, void *context) { + auto *uc = static_cast<ucontext_t *>(context); + const mcontext_t &mc = uc->uc_mcontext; + unw_context_t unwind_context; + unw_cursor_t cursor; + assert(unw_getcontext(&unwind_context) == 0); + assert(unw_init_local(&cursor, &unwind_context) == 0); + + bool found = false; + for (int i = 0; i < 64 && unw_step(&cursor) > 0; ++i) { + if (unw_is_signal_frame(&cursor)) { + found = true; + break; + } + } + assert(found); + check_reg(&cursor, UNW_REG_IP, mc.mc_gpregs.gp_elr); + check_reg(&cursor, UNW_REG_SP, mc.mc_gpregs.gp_sp); + check_reg(&cursor, UNW_AARCH64_LR, mc.mc_gpregs.gp_lr); + for (int i = 0; i < 30; ++i) + check_reg(&cursor, UNW_AARCH64_X0 + i, mc.mc_gpregs.gp_x[i]); + + if (mc.mc_flags & _MC_FP_VALID) { + for (int i = 0; i < 32; ++i) { + unw_fpreg_t value; + assert(unw_get_fpreg(&cursor, UNW_AARCH64_V0 + i, &value) == 0); + assert(memcmp(&value, &mc.mc_fpregs.fp_q[i], sizeof(value)) == 0); + } + } + + assert(unw_step(&cursor) > 0); + assert(!unw_is_signal_frame(&cursor)); + unw_word_t pc; + assert(unw_get_reg(&cursor, UNW_REG_IP, &pc) == 0); + assert(pc >= reinterpret_cast<uintptr_t>(FUNC_START(main_func))); + assert(pc < reinterpret_cast<uintptr_t>(FUNC_END(main_func))); + + // Skip the BRK instruction when returning from the signal handler. + uc->uc_mcontext.mc_gpregs.gp_elr += 4; + handled = handled + 1; +} + +__attribute__((noinline)) static void leaf() { asm volatile("brk #0"); } + +FUNC_ATTR(main_func) int main(int, char **) { + stack_t stack = {}; + stack.ss_size = SIGSTKSZ; + stack.ss_sp = malloc(stack.ss_size); + assert(stack.ss_sp != nullptr); + assert(sigaltstack(&stack, nullptr) == 0); + + struct sigaction action = {}; + action.sa_sigaction = handler; + action.sa_flags = SA_SIGINFO; + assert(sigemptyset(&action.sa_mask) == 0); + assert(sigaction(SIGTRAP, &action, nullptr) == 0); + leaf(); + assert(handled == 1); + + action.sa_flags |= SA_ONSTACK; + assert(sigaction(SIGTRAP, &action, nullptr) == 0); + leaf(); + assert(handled == 2); + + stack.ss_flags = SS_DISABLE; + assert(sigaltstack(&stack, nullptr) == 0); + free(stack.ss_sp); + return 0; +} diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp index 408351662b5217..3835e695220812 100644 --- a/libunwind/test/aarch64_za_unwind.pass.cpp +++ b/libunwind/test/aarch64_za_unwind.pass.cpp @@ -13,7 +13,9 @@ // to disable ZA before resuming from unwinding. // UNSUPPORTED: stdlib=system && target={{.+}}-apple-{{.*}}{{(11|12|13|14|15|26)(\.\d+)*}} +#if !defined(__FreeBSD__) #include <alloca.h> +#endif #include <libunwind.h> #include <stdint.h> #include <stdio.h> `````````` </details> https://github.com/llvm/llvm-project/pull/235065 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
