[libc] Add a minimal implementation of the POSIX fork function. A very simple and minimal implementation of fork is added. Future changes will add more functionality to satisfy POSIX and Linux requirements. An implementation of wait and a few support macros in sys/wait.h have also been added to help with testing the fork function. Reviewed By: lntue, michaelrj Differential Revision: https://reviews.llvm.org/D135131 GitOrigin-RevId: e3638e83db086e77243d0673a4ce8a0b4b330d42
diff --git a/config/linux/api.td b/config/linux/api.td index bd60cb1..8496b29 100644 --- a/config/linux/api.td +++ b/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/config/linux/x86_64/entrypoints.txt b/config/linux/x86_64/entrypoints.txt index 42da9ad..c7e6c73 100644 --- a/config/linux/x86_64/entrypoints.txt +++ b/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/include/CMakeLists.txt b/include/CMakeLists.txt index 70bf4d6..3998d72 100644 --- a/include/CMakeLists.txt +++ b/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/include/llvm-libc-macros/CMakeLists.txt b/include/llvm-libc-macros/CMakeLists.txt index fbf8cef..740802c 100644 --- a/include/llvm-libc-macros/CMakeLists.txt +++ b/include/llvm-libc-macros/CMakeLists.txt
@@ -47,6 +47,14 @@ ) add_header( + sys_wait_macros + HDR + sys-wait-macros.h + DEPENDS + .linux.sys_wait_macros +) + +add_header( time_macros HDR time-macros.h
diff --git a/include/llvm-libc-macros/linux/CMakeLists.txt b/include/llvm-libc-macros/linux/CMakeLists.txt index e0dcb7c..cf6fe04 100644 --- a/include/llvm-libc-macros/linux/CMakeLists.txt +++ b/include/llvm-libc-macros/linux/CMakeLists.txt
@@ -17,6 +17,12 @@ ) add_header( + sys_wait_macros + HDR + sys-wait-macros.h +) + +add_header( time_macros HDR time-macros.h
diff --git a/include/llvm-libc-macros/linux/sys-wait-macros.h b/include/llvm-libc-macros/linux/sys-wait-macros.h new file mode 100644 index 0000000..5b5ad22 --- /dev/null +++ b/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/include/llvm-libc-macros/sys-wait-macros.h b/include/llvm-libc-macros/sys-wait-macros.h new file mode 100644 index 0000000..ea58fcc --- /dev/null +++ b/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/include/sys/wait.h.def b/include/sys/wait.h.def new file mode 100644 index 0000000..b4fcce4 --- /dev/null +++ b/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 <llvm-libc-macros/sys-wait-macros.h> + +%%public_api() + +#endif // LLVM_LIBC_SYS_WAIT_H
diff --git a/spec/posix.td b/spec/posix.td index 3f31723..47e71e9 100644 --- a/spec/posix.td +++ b/spec/posix.td
@@ -343,6 +343,11 @@ [ArgSpec<IntType>] >, FunctionSpec< + "fork", + RetValSpec<PidT>, + [ArgSpec<VoidType>] + >, + FunctionSpec< "fsync", RetValSpec<IntType>, [ArgSpec<IntType>] @@ -963,6 +968,20 @@ ] >; + HeaderSpec SysWait = HeaderSpec< + "sys/wait.h", + [], // Macros + [PidT], + [], // Enumerations + [ + FunctionSpec< + "wait", + RetValSpec<PidT>, + [ArgSpec<IntPtr>] + > + ] + >; + let Headers = [ CType, Dirent, @@ -976,6 +995,7 @@ SysResource, SysStat, SysUtsName, + SysWait, Time, UniStd, String
diff --git a/src/sys/CMakeLists.txt b/src/sys/CMakeLists.txt index 7e6ab88..f8e25cc 100644 --- a/src/sys/CMakeLists.txt +++ b/src/sys/CMakeLists.txt
@@ -3,3 +3,4 @@ add_subdirectory(sendfile) add_subdirectory(stat) add_subdirectory(utsname) +add_subdirectory(wait)
diff --git a/src/sys/wait/CMakeLists.txt b/src/sys/wait/CMakeLists.txt new file mode 100644 index 0000000..9731d7d --- /dev/null +++ b/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/src/sys/wait/linux/CMakeLists.txt b/src/sys/wait/linux/CMakeLists.txt new file mode 100644 index 0000000..88e6f34 --- /dev/null +++ b/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/src/sys/wait/linux/wait.cpp b/src/sys/wait/linux/wait.cpp new file mode 100644 index 0000000..91300f2 --- /dev/null +++ b/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 <errno.h> +#include <sys/syscall.h> // For syscall numbers. +#include <sys/wait.h> + +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/src/sys/wait/wait.h b/src/sys/wait/wait.h new file mode 100644 index 0000000..d49fb21 --- /dev/null +++ b/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 <sys/wait.h> + +namespace __llvm_libc { + +pid_t wait(int *waitstatus); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SYS_WAIT_WAIT_H
diff --git a/src/unistd/CMakeLists.txt b/src/unistd/CMakeLists.txt index 1e54f4c..a460b5d 100644 --- a/src/unistd/CMakeLists.txt +++ b/src/unistd/CMakeLists.txt
@@ -52,6 +52,13 @@ ) add_entrypoint_object( + fork + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.fork +) + +add_entrypoint_object( fsync ALIAS DEPENDS
diff --git a/src/unistd/fork.h b/src/unistd/fork.h new file mode 100644 index 0000000..8d591dd --- /dev/null +++ b/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 <unistd.h> + +namespace __llvm_libc { + +pid_t fork(); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_FORK_H
diff --git a/src/unistd/linux/CMakeLists.txt b/src/unistd/linux/CMakeLists.txt index 0bbde7a..a3f7ef4 100644 --- a/src/unistd/linux/CMakeLists.txt +++ b/src/unistd/linux/CMakeLists.txt
@@ -91,6 +91,21 @@ ) 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 fsync.cpp
diff --git a/src/unistd/linux/fork.cpp b/src/unistd/linux/fork.cpp new file mode 100644 index 0000000..c51eaee --- /dev/null +++ b/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 <errno.h> +#include <sys/syscall.h> // 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/test/integration/src/CMakeLists.txt b/test/integration/src/CMakeLists.txt index 01662b9..c86cd17 100644 --- a/test/integration/src/CMakeLists.txt +++ b/test/integration/src/CMakeLists.txt
@@ -3,3 +3,4 @@ add_subdirectory(stdio) add_subdirectory(stdlib) add_subdirectory(threads) +add_subdirectory(unistd)
diff --git a/test/integration/src/unistd/CMakeLists.txt b/test/integration/src/unistd/CMakeLists.txt new file mode 100644 index 0000000..624f911 --- /dev/null +++ b/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/test/integration/src/unistd/fork_test.cpp b/test/integration/src/unistd/fork_test.cpp new file mode 100644 index 0000000..0cf89f0 --- /dev/null +++ b/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 <errno.h> +#include <signal.h> +#include <sys/wait.h> +#include <unistd.h> + +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; +}