[libc-commits] [libc] e3638e8 - [libc] Add a minimal implementation of the POSIX fork function.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Tue Oct 4 12:12:31 PDT 2022


Author: Siva Chandra Reddy
Date: 2022-10-04T19:12:08Z
New Revision: e3638e83db086e77243d0673a4ce8a0b4b330d42

URL: https://github.com/llvm/llvm-project/commit/e3638e83db086e77243d0673a4ce8a0b4b330d42
DIFF: https://github.com/llvm/llvm-project/commit/e3638e83db086e77243d0673a4ce8a0b4b330d42.diff

LOG: [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

Added: 
    libc/include/llvm-libc-macros/linux/sys-wait-macros.h
    libc/include/llvm-libc-macros/sys-wait-macros.h
    libc/include/sys/wait.h.def
    libc/src/sys/wait/CMakeLists.txt
    libc/src/sys/wait/linux/CMakeLists.txt
    libc/src/sys/wait/linux/wait.cpp
    libc/src/sys/wait/wait.h
    libc/src/unistd/fork.h
    libc/src/unistd/linux/fork.cpp
    libc/test/integration/src/unistd/CMakeLists.txt
    libc/test/integration/src/unistd/fork_test.cpp

Modified: 
    libc/config/linux/api.td
    libc/config/linux/x86_64/entrypoints.txt
    libc/include/CMakeLists.txt
    libc/include/llvm-libc-macros/CMakeLists.txt
    libc/include/llvm-libc-macros/linux/CMakeLists.txt
    libc/spec/posix.td
    libc/src/sys/CMakeLists.txt
    libc/src/unistd/CMakeLists.txt
    libc/src/unistd/linux/CMakeLists.txt
    libc/test/integration/src/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index bd60cb1a572fe..8496b29455d65 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -271,6 +271,10 @@ def SysStatAPI : PublicAPI<"sys/stat.h"> {
                "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
index 42da9adf83070..c7e6c733bf743 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -129,6 +129,9 @@ set(TARGET_LIBC_ENTRYPOINTS
     # 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 @@ if(LLVM_LIBC_FULL_BUILD)
     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
index 70bf4d663f70a..3998d7228e681 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -174,6 +174,7 @@ add_gen_header(
     .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 @@ add_gen_header(
     .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
index fbf8cef92dcdb..740802cd44293 100644
--- a/libc/include/llvm-libc-macros/CMakeLists.txt
+++ b/libc/include/llvm-libc-macros/CMakeLists.txt
@@ -46,6 +46,14 @@ add_header(
     .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
index e0dcb7c9058bc..cf6fe04d28d68 100644
--- a/libc/include/llvm-libc-macros/linux/CMakeLists.txt
+++ b/libc/include/llvm-libc-macros/linux/CMakeLists.txt
@@ -16,6 +16,12 @@ add_header(
     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
index 0000000000000..5b5ad22ecae14
--- /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
index 0000000000000..ea58fccecaffb
--- /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
index 0000000000000..b4fcce4d16529
--- /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 <llvm-libc-macros/sys-wait-macros.h>
+
+%%public_api()
+
+#endif // LLVM_LIBC_SYS_WAIT_H

diff  --git a/libc/spec/posix.td b/libc/spec/posix.td
index 3f31723c43d79..47e71e9cc567d 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -342,6 +342,11 @@ def POSIX : StandardSpec<"POSIX"> {
           RetValSpec<IntType>,
           [ArgSpec<IntType>]
         >,
+        FunctionSpec<
+          "fork",
+          RetValSpec<PidT>,
+          [ArgSpec<VoidType>]
+        >,
         FunctionSpec<
           "fsync",
           RetValSpec<IntType>,
@@ -963,6 +968,20 @@ def POSIX : StandardSpec<"POSIX"> {
       ]
   >;
 
+  HeaderSpec SysWait = HeaderSpec<
+    "sys/wait.h",
+    [], // Macros
+    [PidT],
+    [], // Enumerations
+    [
+      FunctionSpec<
+        "wait",
+        RetValSpec<PidT>,
+        [ArgSpec<IntPtr>]
+      >
+    ]
+  >;
+
   let Headers = [
     CType,
     Dirent,
@@ -976,6 +995,7 @@ def POSIX : StandardSpec<"POSIX"> {
     SysResource,
     SysStat,
     SysUtsName,
+    SysWait,
     Time,
     UniStd,
     String

diff  --git a/libc/src/sys/CMakeLists.txt b/libc/src/sys/CMakeLists.txt
index 7e6ab887b3107..f8e25cc8d4e54 100644
--- a/libc/src/sys/CMakeLists.txt
+++ b/libc/src/sys/CMakeLists.txt
@@ -3,3 +3,4 @@ add_subdirectory(resource)
 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
index 0000000000000..9731d7daa361a
--- /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
index 0000000000000..88e6f34fba692
--- /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
index 0000000000000..91300f2f2f464
--- /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 <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/libc/src/sys/wait/wait.h b/libc/src/sys/wait/wait.h
new file mode 100644
index 0000000000000..d49fb21d9b728
--- /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 <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/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt
index 1e54f4cd4d351..a460b5d18055f 100644
--- a/libc/src/unistd/CMakeLists.txt
+++ b/libc/src/unistd/CMakeLists.txt
@@ -51,6 +51,13 @@ add_entrypoint_object(
     .${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
index 0000000000000..8d591dd8667c5
--- /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 <unistd.h>
+
+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
index 0bbde7a102ab6..a3f7ef4187f01 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -90,6 +90,21 @@ add_entrypoint_object(
     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
index 0000000000000..c51eaeec0d85e
--- /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 <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/libc/test/integration/src/CMakeLists.txt b/libc/test/integration/src/CMakeLists.txt
index 01662b9104383..c86cd178a3ce6 100644
--- a/libc/test/integration/src/CMakeLists.txt
+++ b/libc/test/integration/src/CMakeLists.txt
@@ -3,3 +3,4 @@ add_subdirectory(pthread)
 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
index 0000000000000..624f911cc9357
--- /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
index 0000000000000..0cf89f00b8729
--- /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 <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;
+}


        


More information about the libc-commits mailing list