Index: libc/config/linux/api.td =================================================================== --- libc/config/linux/api.td +++ libc/config/linux/api.td @@ -141,9 +141,28 @@ ]; } +def StructSigactionDefn : TypeDecl<"struct sigaction"> { + let Decl = [{ + struct sigaction { + union { + void (*sa_handler)(int); + void (*sa_action)(int, siginfo_t *, void *); + }; + sigset_t sa_mask; + int sa_flags; + void (*sa_restorer)(void); + }; + }]; +} + def SignalAPI : PublicAPI<"signal.h"> { + let TypeDeclarations = [ + StructSigactionDefn, + ]; + let Functions = [ "raise", + "sigaction", "sigprocmask", "sigemptyset", "sigaddset", Index: libc/config/linux/signal.h.in =================================================================== --- libc/config/linux/signal.h.in +++ libc/config/linux/signal.h.in @@ -8,4 +8,6 @@ %%begin() +#define sigaction __kernel_sigaction #include +#undef sigaction Index: libc/lib/CMakeLists.txt =================================================================== --- libc/lib/CMakeLists.txt +++ libc/lib/CMakeLists.txt @@ -15,6 +15,7 @@ # signal.h entrypoints raise + sigaction sigaddset sigemptyset sigprocmask Index: libc/spec/posix.td =================================================================== --- libc/spec/posix.td +++ libc/spec/posix.td @@ -4,6 +4,12 @@ def RestrictSigSetType : RestrictedPtrType; def ConstRestrictSigSetType : ConstType; +def StructSigaction : NamedType<"struct sigaction">; +def StructSigactionPtr : PtrType; +def ConstStructSigactionPtr : ConstType; +def RestrictStructSigactionPtr : RestrictedPtrType; +def ConstRestrictStructSigactionPtr : ConstType; + def POSIX : StandardSpec<"POSIX"> { NamedType OffTType = NamedType<"off_t">; @@ -137,9 +143,19 @@ HeaderSpec Signal = HeaderSpec< "signal.h", [], // Macros - [], // Types + [ + SigSetType, + StructSigaction, + ], [], // Enumerations [ + FunctionSpec< + "sigaction", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec] + >, FunctionSpec< "sigprocmask", RetValSpec, Index: libc/spec/stdc.td =================================================================== --- libc/spec/stdc.td +++ libc/spec/stdc.td @@ -214,7 +214,6 @@ Macro<"SIGTERM"> ], [ - NamedType<"sigset_t">, SizeTType, ], [], // Enumerations Index: libc/src/signal/linux/CMakeLists.txt =================================================================== --- libc/src/signal/linux/CMakeLists.txt +++ libc/src/signal/linux/CMakeLists.txt @@ -12,6 +12,31 @@ signal_h ) +# TODO: create a better CMake function for this situation. +add_entrypoint_object( + __restore + SRCS + arch/__restore_rt_${LIBC_TARGET_MACHINE}.S + HDRS + ../sigaction.h # HDRS needs to be specify right now so specify anything. + DEPENDS + sys_syscall_h +) + +add_entrypoint_object( + sigaction + SRCS + sigaction.cpp + HDRS + signal.h + ../sigaction.h + DEPENDS + __restore + sys_syscall_h + linux_syscall_h + signal_h +) + add_entrypoint_object( sigprocmask SRCS Index: libc/src/signal/linux/arch/__restore_rt_x86_64.S =================================================================== --- /dev/null +++ libc/src/signal/linux/arch/__restore_rt_x86_64.S @@ -0,0 +1,34 @@ +//===---------------- x86_64 implementation of __restore_rt ---------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// __restore_rt must be handwritten because we must gaurentee that no stack +// allocations are made in the function otherwise the OS will be unable to find +// the signal handlers stack context. Moreover there are various expectations +// that debuggers have which must be fufilled for them to properlly identify +// this code as an sa_restorer trampoline. Among them is the symbolic name +// __restore_rt. However when the symbolic name is not available, gdb will +// match these instructions against what it expects this function to look like. +// Among these is putting the syscall number in rax not eax, which is where +// __llvm_libc::syscall puts syscall numbers. + +#include "include/sys/syscall.h" + +.intel_syntax noprefix +.text + +// This nop works around a gdb bug to make sure it doesn't confuse this function +// with the function right behind it. + nop +.global __restore_rt +.hidden __restore_rt +.type __restore_rt,@function +__restore_rt: + mov rax, SYS_rt_sigreturn + syscall +__restore_rt_end: +.size __restore_rt,__restore_rt_end-__restore_rt Index: libc/src/signal/linux/sigaction.cpp =================================================================== --- /dev/null +++ libc/src/signal/linux/sigaction.cpp @@ -0,0 +1,54 @@ +//===----------------- Linux implementation of sigaction ------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/signal/sigaction.h" +#include "src/errno/llvmlibc_errno.h" +#include "src/signal/linux/signal.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +// TOOD: Some architectures will have their signal trampoline functions in the +// vdso, use those when available. +extern "C" void __restore_rt(); + +template +static void copySigaction(T &dest, const V &source) { + dest.sa_handler = source.sa_handler; + dest.sa_mask = source.sa_mask; + dest.sa_flags = source.sa_flags; + dest.sa_restorer = source.sa_restorer; +} + +int LLVM_LIBC_ENTRYPOINT(sigaction)(int signal, + const struct sigaction *__restrict libc_new, + struct sigaction *__restrict libc_old) { + struct __kernel_sigaction kernel_new; + if (libc_new) { + copySigaction(kernel_new, *libc_new); + if (!(kernel_new.sa_flags & SA_RESTORER)) { + kernel_new.sa_flags |= SA_RESTORER; + kernel_new.sa_restorer = __restore_rt; + } + } + + struct __kernel_sigaction kernel_old; + int ret = syscall(SYS_rt_sigaction, signal, libc_new ? &kernel_new : nullptr, + libc_old ? &kernel_old : nullptr, sizeof(sigset_t)); + if (ret) { + llvmlibc_errno = -ret; + return -1; + } + + if (libc_old) + copySigaction(*libc_old, kernel_old); + return 0; +} + +} // namespace __llvm_libc Index: libc/src/signal/sigaction.h =================================================================== --- /dev/null +++ libc/src/signal/sigaction.h @@ -0,0 +1,21 @@ +//===------------ Implementation header for sigaction --------*- C++ -*--===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SIGNAL_SIGACTION_H +#define LLVM_LIBC_SRC_SIGNAL_SIGACTION_H + +#include "include/signal.h" + +namespace __llvm_libc { + +int sigaction(int signal, const struct sigaction *__restrict libc_new, + struct sigaction *__restrict libc_old); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SIGNAL_SIGACTION_H Index: libc/test/src/signal/CMakeLists.txt =================================================================== --- libc/test/src/signal/CMakeLists.txt +++ libc/test/src/signal/CMakeLists.txt @@ -11,6 +11,23 @@ signal_h ) +add_libc_unittest( + sigaction_test + SUITE + libc_signal_unittests + SRCS + sigaction_test.cpp + DEPENDS + + sigaction + raise + signal_h + __errno_location + + # TOOD remove this when dependencies in add_entrypoint are fixed + __restore +) + add_libc_unittest( sigprocmask_test SUITE Index: libc/test/src/signal/sigaction_test.cpp =================================================================== --- /dev/null +++ libc/test/src/signal/sigaction_test.cpp @@ -0,0 +1,65 @@ +//===----------------------- Unittests for sigaction ----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "include/errno.h" +#include "include/signal.h" +#include "src/signal/raise.h" +#include "src/signal/sigaction.h" + +#include "utils/UnitTest/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +using __llvm_libc::testing::ErrnoSetterMatcher::Fails; +using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + +TEST(Sigaction, Invalid) { + // -1 is a much larger signal that NSIG, so this should fail. + EXPECT_THAT(__llvm_libc::sigaction(-1, nullptr, nullptr), Fails(EINVAL)); +} + +// SIGKILL cannot have its action changed, but it can be examined. +TEST(Sigaction, Sigkill) { + struct sigaction action; + EXPECT_THAT(__llvm_libc::sigaction(SIGKILL, nullptr, &action), Succeeds()); + EXPECT_THAT(__llvm_libc::sigaction(SIGKILL, &action, nullptr), Fails(EINVAL)); +} + +static int sigusr1Count; +static bool correctSignal; + +TEST(Sigaction, CustomAction) { + // Zero this incase tests get run multiple times in the future. + sigusr1Count = 0; + + struct sigaction action; + EXPECT_THAT(__llvm_libc::sigaction(SIGUSR1, nullptr, &action), Succeeds()); + + action.sa_handler = +[](int signal) { + correctSignal = signal == SIGUSR1; + sigusr1Count++; + }; + EXPECT_THAT(__llvm_libc::sigaction(SIGUSR1, &action, nullptr), Succeeds()); + + __llvm_libc::raise(SIGUSR1); + EXPECT_EQ(sigusr1Count, 1); + EXPECT_TRUE(correctSignal); + + action.sa_handler = SIG_DFL; + EXPECT_THAT(__llvm_libc::sigaction(SIGUSR1, &action, nullptr), Succeeds()); + + EXPECT_DEATH([] { __llvm_libc::raise(SIGUSR1); }, SIGUSR1); +} + +TEST(Sigaction, Ignore) { + struct sigaction action; + EXPECT_THAT(__llvm_libc::sigaction(SIGUSR1, nullptr, &action), Succeeds()); + action.sa_handler = SIG_IGN; + EXPECT_THAT(__llvm_libc::sigaction(SIGUSR1, &action, nullptr), Succeeds()); + + EXPECT_EXITS([] {__llvm_libc::raise(SIGUSR1); }, 0); +}