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 @@ -271,6 +271,10 @@ "struct timespec", "blksize_t", "blkcnt_t", "struct stat"]; } +def SysWaitAPI : PublicAPI<"sys/wait.h"> { + let Types = ["pid_t"]; +} + def SysSendfileAPI : PublicAPI<"sys/sendfile.h"> { let Types = ["off_t", "size_t", "ssize_t"]; } diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -129,6 +129,9 @@ # sys/utsname.h entrypoints libc.src.sys.utsname.uname + # sys/wait.h entrypoints + libc.src.sys.wait.wait + # unistd.h entrypoints libc.src.unistd.access libc.src.unistd.chdir @@ -401,6 +404,9 @@ libc.src.time.mktime libc.src.time.nanosleep libc.src.time.clock_gettime + + # unistd.h entrypoints + libc.src.unistd.fork ) endif() diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -174,6 +174,7 @@ .llvm_libc_common_h .llvm-libc-macros.file_seek_macros .llvm-libc-macros.unistd_macros + .llvm-libc-types.off_t .llvm-libc-types.pid_t .llvm-libc-types.size_t .llvm-libc-types.ssize_t @@ -273,6 +274,16 @@ .llvm-libc-types.struct_utsname ) +add_gen_header( + sys_wait + DEF_FILE sys/wait.h.def + GEN_HDR sys/wait.h + DEPENDS + .llvm_libc_common_h + .llvm-libc-macros.sys_wait_macros + .llvm-libc-types.pid_t +) + if(NOT LLVM_LIBC_FULL_BUILD) # We don't install headers in non-fullbuild mode. return() diff --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt --- a/libc/include/llvm-libc-macros/CMakeLists.txt +++ b/libc/include/llvm-libc-macros/CMakeLists.txt @@ -46,6 +46,14 @@ .linux.sys_resource_macros ) +add_header( + sys_wait_macros + HDR + sys-wait-macros.h + DEPENDS + .linux.sys_wait_macros +) + add_header( time_macros HDR diff --git a/libc/include/llvm-libc-macros/linux/CMakeLists.txt b/libc/include/llvm-libc-macros/linux/CMakeLists.txt --- a/libc/include/llvm-libc-macros/linux/CMakeLists.txt +++ b/libc/include/llvm-libc-macros/linux/CMakeLists.txt @@ -16,6 +16,12 @@ sys-stat-macros.h ) +add_header( + sys_wait_macros + HDR + sys-wait-macros.h +) + add_header( time_macros HDR diff --git a/libc/include/llvm-libc-macros/linux/sys-wait-macros.h b/libc/include/llvm-libc-macros/linux/sys-wait-macros.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-macros/linux/sys-wait-macros.h @@ -0,0 +1,17 @@ +//===-- Definition of macros from sys/wait.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_MACROS_LINUX_SYS_WAIT_MACROS_H +#define __LLVM_LIBC_MACROS_LINUX_SYS_WAIT_MACROS_H + +// Wait status info macros +#define WTERMSIG(status) (((status)&0x7F)) +#define WIFEXITED(status) (WTERMSIG(status) == 0) +#define WEXITSTATUS(status) (((status)&0xFF00) >> 8) + +#endif // __LLVM_LIBC_MACROS_LINUX_SYS_WAIT_MACROS_H diff --git a/libc/include/llvm-libc-macros/sys-wait-macros.h b/libc/include/llvm-libc-macros/sys-wait-macros.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-macros/sys-wait-macros.h @@ -0,0 +1,16 @@ +//===-- Macros defined in sys/wait.h header file --------------------------===// +// +// 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_MACROS_SYS_WAIT_MACROS_H +#define __LLVM_LIBC_MACROS_SYS_WAIT_MACROS_H + +#ifdef __linux__ +#include "linux/sys-wait-macros.h" +#endif + +#endif // __LLVM_LIBC_MACROS_SYS_WAIT_MACROS_H diff --git a/libc/include/sys/wait.h.def b/libc/include/sys/wait.h.def new file mode 100644 --- /dev/null +++ b/libc/include/sys/wait.h.def @@ -0,0 +1,18 @@ +//===-- POSIX header wait.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_SYS_WAIT_H +#define LLVM_LIBC_SYS_WAIT_H + +#include <__llvm-libc-common.h> + +#include + +%%public_api() + +#endif // LLVM_LIBC_SYS_WAIT_H diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -342,6 +342,11 @@ RetValSpec, [ArgSpec] >, + FunctionSpec< + "fork", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "fsync", RetValSpec, @@ -963,6 +968,20 @@ ] >; + HeaderSpec SysWait = HeaderSpec< + "sys/wait.h", + [], // Macros + [PidT], + [], // Enumerations + [ + FunctionSpec< + "wait", + RetValSpec, + [ArgSpec] + > + ] + >; + let Headers = [ CType, Dirent, @@ -976,6 +995,7 @@ SysResource, SysStat, SysUtsName, + SysWait, Time, UniStd, String diff --git a/libc/src/sys/CMakeLists.txt b/libc/src/sys/CMakeLists.txt --- a/libc/src/sys/CMakeLists.txt +++ b/libc/src/sys/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory(sendfile) add_subdirectory(stat) add_subdirectory(utsname) +add_subdirectory(wait) diff --git a/libc/src/sys/wait/CMakeLists.txt b/libc/src/sys/wait/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/sys/wait/CMakeLists.txt @@ -0,0 +1,10 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) +endif() + +add_entrypoint_object( + wait + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.wait +) diff --git a/libc/src/sys/wait/linux/CMakeLists.txt b/libc/src/sys/wait/linux/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/sys/wait/linux/CMakeLists.txt @@ -0,0 +1,13 @@ +add_entrypoint_object( + wait + SRCS + wait.cpp + HDRS + ../wait.h + DEPENDS + libc.include.errno + libc.include.sys_wait + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) diff --git a/libc/src/sys/wait/linux/wait.cpp b/libc/src/sys/wait/linux/wait.cpp new file mode 100644 --- /dev/null +++ b/libc/src/sys/wait/linux/wait.cpp @@ -0,0 +1,34 @@ +//===-- Linux implementation of wait --------------------------------------===// +// +// 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/sys/wait/wait.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include // For syscall numbers. +#include + +namespace __llvm_libc { + +// The implementation of wait here is very minimal. We will add more +// functionality and standard compliance in future. + +LLVM_LIBC_FUNCTION(pid_t, wait, (int *wait_status)) { + pid_t pid = __llvm_libc::syscall_impl(SYS_wait4, -1, wait_status, 0, 0); + if (pid < 0) { + // Error case, a child process was not created. + errno = -pid; + return -1; + } + + return pid; +} + +} // namespace __llvm_libc diff --git a/libc/src/sys/wait/wait.h b/libc/src/sys/wait/wait.h new file mode 100644 --- /dev/null +++ b/libc/src/sys/wait/wait.h @@ -0,0 +1,20 @@ +//===-- Implementation header for wait --------------------------*- 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_SYS_WAIT_WAIT_H +#define LLVM_LIBC_SRC_SYS_WAIT_WAIT_H + +#include + +namespace __llvm_libc { + +pid_t wait(int *waitstatus); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SYS_WAIT_WAIT_H diff --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt --- a/libc/src/unistd/CMakeLists.txt +++ b/libc/src/unistd/CMakeLists.txt @@ -51,6 +51,13 @@ .${LIBC_TARGET_OS}.fchdir ) +add_entrypoint_object( + fork + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.fork +) + add_entrypoint_object( fsync ALIAS diff --git a/libc/src/unistd/fork.h b/libc/src/unistd/fork.h new file mode 100644 --- /dev/null +++ b/libc/src/unistd/fork.h @@ -0,0 +1,20 @@ +//===-- Implementation header for fork --------------------------*- 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_UNISTD_FORK_H +#define LLVM_LIBC_SRC_UNISTD_FORK_H + +#include + +namespace __llvm_libc { + +pid_t fork(); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_FORK_H diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt --- a/libc/src/unistd/linux/CMakeLists.txt +++ b/libc/src/unistd/linux/CMakeLists.txt @@ -90,6 +90,21 @@ libc.src.errno.errno ) +add_entrypoint_object( + fork + SRCS + fork.cpp + HDRS + ../fork.h + DEPENDS + libc.include.errno + libc.include.unistd + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.__support.threads.thread + libc.src.errno.errno +) + add_entrypoint_object( fsync SRCS diff --git a/libc/src/unistd/linux/fork.cpp b/libc/src/unistd/linux/fork.cpp new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linux/fork.cpp @@ -0,0 +1,49 @@ +//===-- Linux implementation of fork --------------------------------------===// +// +// 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/unistd/fork.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" +#include "src/__support/threads/thread.h" // For thread self object + +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +// The implementation of fork here is very minimal. We will add more +// functionality and standard compliance in future. + +LLVM_LIBC_FUNCTION(pid_t, fork, (void)) { +#ifdef SYS_fork + pid_t ret = __llvm_libc::syscall_impl(SYS_fork); +#elif defined(SYS_clone) + pid_t ret = __llvm_libc::syscall_impl(SYS_clone, SIGCHLD, 0); +#else +#error "SYS_fork or SYS_clone not available." +#endif + if (ret == 0) { + // Return value is 0 in the child process. + // The child is created with a single thread whose self object will be a + // copy of parent process' thread which called fork. So, we have to fix up + // the child process' self object with the new process' tid. + self.attrib->tid = __llvm_libc::syscall_impl(SYS_gettid); + return 0; + } + + if (ret < 0) { + // Error case, a child process was not created. + errno = -ret; + return -1; + } + + return ret; +} + +} // namespace __llvm_libc diff --git a/libc/test/integration/src/CMakeLists.txt b/libc/test/integration/src/CMakeLists.txt --- a/libc/test/integration/src/CMakeLists.txt +++ b/libc/test/integration/src/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory(stdio) add_subdirectory(stdlib) add_subdirectory(threads) +add_subdirectory(unistd) diff --git a/libc/test/integration/src/unistd/CMakeLists.txt b/libc/test/integration/src/unistd/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/integration/src/unistd/CMakeLists.txt @@ -0,0 +1,20 @@ +add_custom_target(unistd-integration-tests) +add_dependencies(libc-integration-tests unistd-integration-tests) + +add_integration_test( + fork_test + SUITE + unistd-integration-tests + SRCS + fork_test.cpp + LOADER + libc.loader.linux.crt1 + DEPENDS + libc.include.errno + libc.include.signal + libc.include.sys_wait + libc.include.unistd + libc.src.signal.raise + libc.src.sys.wait.wait + libc.src.unistd.fork +) diff --git a/libc/test/integration/src/unistd/fork_test.cpp b/libc/test/integration/src/unistd/fork_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/integration/src/unistd/fork_test.cpp @@ -0,0 +1,49 @@ +//===-- Unittests for fork ------------------------------------------------===// +// +// 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/sys/wait/wait.h" +#include "src/unistd/fork.h" + +#include "utils/IntegrationTest/test.h" + +#include +#include +#include +#include + +void fork_and_wait_normal_exit() { + pid_t pid = __llvm_libc::fork(); + if (pid == 0) + return; // Just end without any thing special. + ASSERT_TRUE(pid > 0); + int status; + pid_t cpid = __llvm_libc::wait(&status); + ASSERT_TRUE(cpid > 0); + ASSERT_EQ(cpid, pid); + ASSERT_TRUE(WIFEXITED(status)); +} + +void fork_and_wait_signal_exit() { + pid_t pid = __llvm_libc::fork(); + if (pid == 0) + __llvm_libc::raise(SIGUSR1); + ASSERT_TRUE(pid > 0); + int status; + pid_t cpid = __llvm_libc::wait(&status); + ASSERT_TRUE(cpid > 0); + ASSERT_EQ(cpid, pid); + ASSERT_FALSE(WIFEXITED(status)); + ASSERT_TRUE(WTERMSIG(status) == SIGUSR1); +} + +TEST_MAIN(int argc, char **argv, char **envp) { + fork_and_wait_normal_exit(); + fork_and_wait_signal_exit(); + return 0; +}