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 @@ -84,6 +84,13 @@ ]; } +def StdlibAPI : PublicAPI<"stdlib.h"> { + let Functions = [ + "_Exit", + "abort", + ]; +} + def ErrnoAPI : PublicAPI<"errno.h"> { let Macros = [ ErrnoMacro, diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -63,6 +63,14 @@ ../config/${LIBC_TARGET_OS}/signal.h.in ) +add_gen_header( + stdlib_h + DEF_FILE stdlib.h.def + GEN_HDR stdlib.h + DEPENDS + llvm_libc_common_h +) + # 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/__llvm-libc-common.h b/libc/include/__llvm-libc-common.h --- a/libc/include/__llvm-libc-common.h +++ b/libc/include/__llvm-libc-common.h @@ -17,6 +17,9 @@ #undef __END_C_DECLS #define __END_C_DECLS } +#undef _Noreturn +#define _Noreturn [[noreturn]] + #else // not __cplusplus #undef __BEGIN_C_DECLS diff --git a/libc/include/stdlib.h.def b/libc/include/stdlib.h.def new file mode 100644 --- /dev/null +++ b/libc/include/stdlib.h.def @@ -0,0 +1,16 @@ +//===---------------- C standard library header stdlib.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_STDLIB_H +#define LLVM_LIBC_STDLIB_H + +#include <__llvm-libc-common.h> + +%%public_api() + +#endif // LLVM_LIBC_STDLIB_H diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt --- a/libc/lib/CMakeLists.txt +++ b/libc/lib/CMakeLists.txt @@ -18,6 +18,10 @@ sigaddset sigemptyset sigprocmask + + # stdlib.h entrypoints + _Exit + abort ) add_entrypoint_library( diff --git a/libc/spec/spec.td b/libc/spec/spec.td --- a/libc/spec/spec.td +++ b/libc/spec/spec.td @@ -48,6 +48,9 @@ def VoidPtr : PtrType; def SizeTType : NamedType<"size_t">; +// _Noreturn is really not a type, but it is convenient to treat it as a type. +def NoReturn : NamedType<"_Noreturn void">; + class Macro { string Name = name; } diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -178,6 +178,17 @@ ] >; + HeaderSpec StdLib = HeaderSpec< + "stdlib.h", + [], // Macros + [], // Types + [], // Enumerations + [ + FunctionSpec<"abort", RetValSpec, [ArgSpec]>, + FunctionSpec<"_Exit", RetValSpec, [ArgSpec]>, + ] + >; + HeaderSpec Errno = HeaderSpec< "errno.h", [ @@ -278,6 +289,7 @@ Math, String, StdIO, + StdLib, Signal, Threads, ]; diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt --- a/libc/src/CMakeLists.txt +++ b/libc/src/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(errno) add_subdirectory(math) add_subdirectory(signal) +add_subdirectory(stdlib) add_subdirectory(string) # TODO: Add this target conditional to the target OS. add_subdirectory(sys) diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/stdlib/CMakeLists.txt @@ -0,0 +1,15 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${LIBC_TARGET_OS}) +endif() + +add_entrypoint_object( + abort + SRCS + abort.cpp + HDRS + abort.h + DEPENDS + raise + _Exit + stdlib_h +) diff --git a/libc/src/stdlib/_Exit.h b/libc/src/stdlib/_Exit.h new file mode 100644 --- /dev/null +++ b/libc/src/stdlib/_Exit.h @@ -0,0 +1,18 @@ +//===----------------- Implementation header for _Exit ---------*- 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__EXIT_H +#define LLVM_LIBC_SRC__EXIT_H + +namespace __llvm_libc { + +[[noreturn]] void _Exit(int status); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC__EXIT_H diff --git a/libc/src/stdlib/abort.h b/libc/src/stdlib/abort.h new file mode 100644 --- /dev/null +++ b/libc/src/stdlib/abort.h @@ -0,0 +1,18 @@ +//===----------------- Implementation header for abort ---------*- 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_ABORT_H +#define LLVM_LIBC_SRC_ABORT_H + +namespace __llvm_libc { + +[[noreturn]] void abort(); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_ABORT_H diff --git a/libc/src/stdlib/abort.cpp b/libc/src/stdlib/abort.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdlib/abort.cpp @@ -0,0 +1,30 @@ +//===---------------------- Implementation of abort -----------------------===// +// +// 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/__support/common.h" +#include "src/signal/raise.h" +#include "src/stdlib/_Exit.h" + +#include "src/stdlib/abort.h" + +namespace __llvm_libc { + +void LLVM_LIBC_ENTRYPOINT(abort)() { + // TODO: When sigprocmask and sigaction land: + // Unblock SIGABRT, raise it, if it was ignored or the handler returned, + // change its action to SIG_DFL, raise it again. + // TODO: When C11 mutexes land: + // Aquire recursive mutex (in case the current signal handler for SIGABRT + // itself calls abort we don't want to deadlock on the same thread trying + // to aquire it's own mutex.) + __llvm_libc::raise(SIGABRT); + __llvm_libc::raise(SIGKILL); + __llvm_libc::_Exit(127); +} + +} // namespace __llvm_libc diff --git a/libc/src/stdlib/linux/CMakeLists.txt b/libc/src/stdlib/linux/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/stdlib/linux/CMakeLists.txt @@ -0,0 +1,11 @@ +add_entrypoint_object( + _Exit + SRCS + _Exit.cpp + HDRS + ../_Exit.h + DEPENDS + sys_syscall_h + linux_syscall_h + stdlib_h +) diff --git a/libc/src/stdlib/linux/_Exit.cpp b/libc/src/stdlib/linux/_Exit.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdlib/linux/_Exit.cpp @@ -0,0 +1,24 @@ +//===------------------- Linux Implementation of _Exit --------------------===// +// +// 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 "config/linux/syscall.h" // For internal syscall function. +#include "include/sys/syscall.h" // For syscall numbers. +#include "src/__support/common.h" + +#include "src/stdlib/_Exit.h" + +namespace __llvm_libc { + +void LLVM_LIBC_ENTRYPOINT(_Exit)(int status) { + for (;;) { + __llvm_libc::syscall(SYS_exit_group, status); + __llvm_libc::syscall(SYS_exit, status); + } +} + +} // namespace __llvm_libc 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,4 +1,5 @@ add_subdirectory(errno) add_subdirectory(signal) +add_subdirectory(stdlib) add_subdirectory(string) add_subdirectory(sys) diff --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/stdlib/CMakeLists.txt @@ -0,0 +1,26 @@ +add_libc_testsuite(libc_stdlib_unittests) + +add_libc_unittest( + _Exit_test + SUITE + libc_stdlib_unittests + SRCS + _Exit_test.cpp + DEPENDS + stdlib_h + _Exit +) + +add_libc_unittest( + abort_test + SUITE + libc_stdlib_unittests + SRCS + abort_test.cpp + DEPENDS + stdlib_h + signal_h + abort + _Exit + raise +) diff --git a/libc/test/src/stdlib/_Exit_test.cpp b/libc/test/src/stdlib/_Exit_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdlib/_Exit_test.cpp @@ -0,0 +1,16 @@ +//===----------------------- Unittests for _Exit --------------------------===// +// +// 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/stdlib.h" +#include "src/stdlib/_Exit.h" +#include "utils/UnitTest/Test.h" + +TEST(Stdlib, _Exit) { + EXPECT_EXITS([] { __llvm_libc::_Exit(1); }, 1); + EXPECT_EXITS([] { __llvm_libc::_Exit(65); }, 65); +} diff --git a/libc/test/src/stdlib/abort_test.cpp b/libc/test/src/stdlib/abort_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdlib/abort_test.cpp @@ -0,0 +1,18 @@ +//===----------------------- Unittests for abort --------------------------===// +// +// 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 "include/stdlib.h" +#include "src/stdlib/abort.h" +#include "utils/UnitTest/Test.h" + +TEST(Stdlib, abort) { + // -1 matches against any signal, which is necessary for now until + // __llvm_libc::abort() unblocks SIGABRT. + EXPECT_DEATH([] { __llvm_libc::abort(); }, -1); +}