diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -133,3 +133,13 @@ "munmap", ]; } + +def SignalAPI : PublicAPI<"signal.h"> { + let TypeDeclarations = [ + SizeT, // This is needed by . + ]; + + let Functions = [ + "raise", + ]; +} diff --git a/libc/config/linux/signal.h.in b/libc/config/linux/signal.h.in new file mode 100644 --- /dev/null +++ b/libc/config/linux/signal.h.in @@ -0,0 +1,11 @@ +//===---------------- Linux specific signal.h definitions -----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +%%begin() + +#include diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -45,6 +45,16 @@ ../config/${LIBC_TARGET_OS}/errno.h.in ) +add_gen_header( + signal_h + DEF_FILE signal.h.def + PARAMS + platform_signal=../config/${LIBC_TARGET_OS}/signal.h.in + GEN_HDR signal.h + DATA_FILES + ../config/${LIBC_TARGET_OS}/signal.h.in +) + # TODO: Not all platforms will have a include/sys directory. Add the sys # directory and the targets for sys/*.h files conditional to the OS requiring # them. diff --git a/libc/include/signal.h.def b/libc/include/signal.h.def new file mode 100644 --- /dev/null +++ b/libc/include/signal.h.def @@ -0,0 +1,18 @@ +//===---------------- C standard library header signal.h ------------------===// +// +// 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_SIGNAL_H +#define LLVM_LIBC_SIGNAL_H + +#include <__llvm-libc-common.h> + +%%public_api() + +%%include_file(${platform_signal}) + +#endif // LLVM_LIBC_SIGNAL_H diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt --- a/libc/lib/CMakeLists.txt +++ b/libc/lib/CMakeLists.txt @@ -12,6 +12,9 @@ # sys/mman.h entrypoints mmap munmap + + # signal.h entrypoints + raise ) add_entrypoint_library( diff --git a/libc/spec/linux.td b/libc/spec/linux.td --- a/libc/spec/linux.td +++ b/libc/spec/linux.td @@ -67,8 +67,53 @@ [] // Functions >; + HeaderSpec Signal = HeaderSpec< + "signal.h", + [ + Macro<"NSIG">, + + Macro<"SIGHUP">, + Macro<"SIGINT">, + Macro<"SIGQUIT">, + Macro<"SIGILL">, + Macro<"SIGTRAP">, + Macro<"SIGABRT">, + Macro<"SIGIOT">, + Macro<"SIGBUS">, + Macro<"SIGFPE">, + Macro<"SIGKILL">, + Macro<"SIGUSR1">, + Macro<"SIGSEGV">, + Macro<"SIGUSR2">, + Macro<"SIGPIPE">, + Macro<"SIGALRM">, + Macro<"SIGTERM">, + Macro<"SIGSTKFLT">, + Macro<"SIGCHLD">, + Macro<"SIGCONT">, + Macro<"SIGSTOP">, + Macro<"SIGTSTP">, + Macro<"SIGTTIN">, + Macro<"SIGTTOU">, + Macro<"SIGURG">, + Macro<"SIGXCPU">, + Macro<"SIGXFSZ">, + Macro<"SIGVTALRM">, + Macro<"SIGPROF">, + Macro<"SIGWINCH">, + Macro<"SIGIO">, + Macro<"SIGPOLL">, + Macro<"SIGPWR">, + Macro<"SIGSYS">, + Macro<"SIGUNUSED">, + ], + [], // Types + [] // Functions + >; + let Headers = [ Errno, SysMMan, + Signal, ]; } diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -179,10 +179,34 @@ [] // Functions >; + HeaderSpec Signal = HeaderSpec< + "signal.h", + [ + Macro<"SIG_BLOCK">, + Macro<"SIG_UNBLOCK">, + Macro<"SIG_SETMASK">, + + Macro<"SIGABRT">, + Macro<"SIGFPE">, + Macro<"SIGILL">, + Macro<"SIGINT">, + Macro<"SIGSEGV">, + Macro<"SIGTERM"> + ], + [ + NamedType<"sigset_t">, + SizeTType, + ], + [ + FunctionSpec<"raise", RetValSpec, [ArgSpec]>, + ] + >; + let Headers = [ Errno, Math, String, StdIO, + Signal, ]; } diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt --- a/libc/src/CMakeLists.txt +++ b/libc/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(errno) add_subdirectory(math) +add_subdirectory(signal) add_subdirectory(string) # TODO: Add this target conditional to the target OS. add_subdirectory(sys) diff --git a/libc/src/signal/CMakeLists.txt b/libc/src/signal/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/signal/CMakeLists.txt @@ -0,0 +1,4 @@ + +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${LIBC_TARGET_OS}) +endif() diff --git a/libc/src/signal/linux/CMakeLists.txt b/libc/src/signal/linux/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/signal/linux/CMakeLists.txt @@ -0,0 +1,14 @@ + +add_entrypoint_object( + raise + SRCS + raise.cpp + HDRS + signal.h + ../raise.h + DEPENDS + sys_syscall_h + linux_syscall_h + __errno_location + signal_h +) diff --git a/libc/src/signal/linux/raise.cpp b/libc/src/signal/linux/raise.cpp new file mode 100644 --- /dev/null +++ b/libc/src/signal/linux/raise.cpp @@ -0,0 +1,26 @@ +//===------------------ Linux implementation of signal --------------------===// +// +// 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/raise.h" +#include "src/signal/linux/signal.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +int LLVM_LIBC_ENTRYPOINT(raise)(int sig) { + __llvm_libc::Sigset sigset; + int got = __llvm_libc::block_all_signals(sigset); + long pid = __llvm_libc::syscall(SYS_getpid); + long tid = __llvm_libc::syscall(SYS_gettid); + int ret = __llvm_libc::syscall(SYS_tgkill, pid, tid, sig); + __llvm_libc::restore_signals(sigset); + return ret; +} + +} // namespace __llvm_libc diff --git a/libc/src/signal/linux/signal.h b/libc/src/signal/linux/signal.h new file mode 100644 --- /dev/null +++ b/libc/src/signal/linux/signal.h @@ -0,0 +1,49 @@ +//===----------------- Internal header for Linux signals ------------------===// +// +// 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_LINUX_SIGNAL_H +#define LLVM_LIBC_SRC_SIGNAL_LINUX_SIGNAL_H + +#include "config/linux/syscall.h" // For internal syscall function. +#include "include/sys/syscall.h" // For syscall numbers. + +#include "include/signal.h" + +static_assert(sizeof(sigset_t) * 8 >= NSIG, "sigset_t cannot hold all signals"); + +namespace __llvm_libc { + +// Using this internally defined type will make it easier in the future to port +// to different architectures. +struct Sigset { + sigset_t nativeSigset; + + constexpr static Sigset fullset() { return {-1UL}; } + + operator sigset_t() const { return nativeSigset; } +}; + +constexpr static Sigset all = Sigset::fullset(); + +static inline int block_all_signals(Sigset &set) { + sigset_t nativeSigset = all; + sigset_t oldSet = set; + return __llvm_libc::syscall(SYS_rt_sigprocmask, SIG_BLOCK, &nativeSigset, + &oldSet, sizeof(sigset_t)); +} + +static inline int restore_signals(const Sigset &set) { + sigset_t nativeSigset = set; + return __llvm_libc::syscall(SYS_rt_sigprocmask, SIG_SETMASK, + &nativeSigset, nullptr, + sizeof(sigset_t)); +} + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SIGNAL_LINUX_SIGNAL_H diff --git a/libc/src/signal/raise.h b/libc/src/signal/raise.h new file mode 100644 --- /dev/null +++ b/libc/src/signal/raise.h @@ -0,0 +1,20 @@ +//===------------- Implementation header for raise function ------ *-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_RAISE_H +#define LLVM_LIBC_SRC_SIGNAL_RAISE_H + +#include "include/signal.h" + +namespace __llvm_libc { + +int raise(int sig); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SIGNAL_RAISE_H diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(errno) +add_subdirectory(signal) add_subdirectory(string) add_subdirectory(sys) diff --git a/libc/test/src/signal/CMakeLists.txt b/libc/test/src/signal/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/signal/CMakeLists.txt @@ -0,0 +1,12 @@ +add_libc_testsuite(libc_signal_unittests) + +add_libc_unittest( + raise_test + SUITE + libc_signal_unittests + SRCS + raise_test.cpp + DEPENDS + raise + signal_h +) diff --git a/libc/test/src/signal/raise_test.cpp b/libc/test/src/signal/raise_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/signal/raise_test.cpp @@ -0,0 +1,17 @@ +//===----------------------- Unittests for raise --------------------------===// +// +// 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/signal.h" +#include "src/signal/raise.h" +#include "utils/UnitTest/Test.h" + +TEST(SignalTest, Raise) { + // SIGCONT is ingored unless stopped, so we can use it to check the return + // value of raise without needing to block. + EXPECT_EQ(__llvm_libc::raise(SIGCONT), 0); +}