libc.src.__support.threads.thread
libc.src.stdlib.atexit
libc.src.stdlib.exit
- libc.src.unistd.environ)
+ libc.src.unistd.environ
+ libc.utils.IntegrationTest.test)
list(REMOVE_DUPLICATES fq_deps_list)
# We don't want memory functions to be dependencies on integration tests.
}
def SpawnAPI : PublicAPI<"spawn.h"> {
- let Types = ["mode_t", "posix_spawn_file_actions_t"];
+ let Types = ["mode_t", "pid_t", "posix_spawnattr_t", "posix_spawn_file_actions_t"];
}
libc.src.signal.signal
# spawn.h entrypoints
+ libc.src.spawn.posix_spawn
libc.src.spawn.posix_spawn_file_actions_addclose
libc.src.spawn.posix_spawn_file_actions_adddup2
libc.src.spawn.posix_spawn_file_actions_addopen
DEPENDS
.llvm_libc_common_h
.llvm-libc-types.mode_t
+ .llvm-libc-types.pid_t
+ .llvm-libc-types.posix_spawnattr_t
.llvm-libc-types.posix_spawn_file_actions_t
)
add_header(once_flag HDR once_flag.h DEPENDS .__futex_word)
add_header(pid_t HDR pid_t.h)
add_header(posix_spawn_file_actions_t HDR posix_spawn_file_actions_t.h)
+add_header(posix_spawnattr_t HDR posix_spawnattr_t.h)
add_header(pthread_attr_t HDR pthread_attr_t.h DEPENDS .size_t)
add_header(pthread_key_t HDR pthread_key_t.h)
add_header(pthread_mutex_t HDR pthread_mutex_t.h DEPENDS .__futex_word .__mutex_type)
--- /dev/null
+//===-- Definition of type posix_spawn_file_actions_t ---------------------===//
+//
+// 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_TYPES_POSIX_SPAWNATTR_T_H
+#define __LLVM_LIBC_TYPES_POSIX_SPAWNATTR_T_H
+
+typedef struct {
+ // This data structure will be populated as required.
+} posix_spawnattr_t;
+
+#endif // __LLVM_LIBC_TYPES_POSIX_SPAWNATTR_T_H
def PosixSpawnFileActionsT : NamedType<"posix_spawn_file_actions_t">;
def PosixSpawnFileActionsTPtr : PtrType<PosixSpawnFileActionsT>;
+def ConstPosixSpawnFileActionsTPtr : ConstType<PosixSpawnFileActionsTPtr>;
def PosixSpawnFileActionsTRestrictedPtr : RestrictedPtrType<PosixSpawnFileActionsT>;
+def PosixSpawnAttrT : NamedType<"posix_spawnattr_t">;
+def RestrictedPosixSpawnAttrTPtrType : RestrictedPtrType<PosixSpawnAttrT>;
+
def POSIX : StandardSpec<"POSIX"> {
PtrType CharPtr = PtrType<CharType>;
RestrictedPtrType RestrictedCharPtr = RestrictedPtrType<CharType>;
HeaderSpec Spawn = HeaderSpec<
"spawn.h",
[], // Macros
- [PosixSpawnFileActionsT, ModeTType],
+ [ModeTType, PosixSpawnAttrT, PidT, PosixSpawnFileActionsT],
[], // Enumerations
[
FunctionSpec<
RetValSpec<IntType>,
[ArgSpec<PosixSpawnFileActionsTPtr>]
>,
+ FunctionSpec<
+ "posix_spawn",
+ RetValSpec<IntType>,
+ [ArgSpec<RestrictedPidTPtr>, ArgSpec<ConstCharRestrictedPtr>,
+ ArgSpec<PosixSpawnFileActionsTPtr>, ArgSpec<RestrictedPosixSpawnAttrTPtrType>,
+ ArgSpec<ConstCharRestrictedPtrPtr>, ArgSpec<ConstCharRestrictedPtrPtr>]
+ >,
]
>;
def CharRestrictedPtr : RestrictedPtrType<CharType>;
def CharRestrictedPtrPtr : RestrictedPtrType<CharPtr>;
def ConstCharRestrictedPtr : ConstType<CharRestrictedPtr>;
+def ConstCharRestrictedPtrPtr : PtrType<ConstCharRestrictedPtr>;
def OnceFlagType : NamedType<"once_flag">;
def OnceFlagTypePtr : PtrType<OnceFlagType>;
def PThreadTType : NamedType<"pthread_t">;
def PidT : NamedType<"pid_t">;
+def RestrictedPidTPtr : RestrictedPtrType<PidT>;
+
def StructRUsage : NamedType<"struct rusage">;
def StructRUsagePtr : PtrType<StructRUsage>;
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+endif()
+
add_header_library(
file_actions
HDRS
libc.include.errno
libc.include.spawn
)
+
+add_entrypoint_object(
+ posix_spawn
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.posix_spawn
+)
--- /dev/null
+add_entrypoint_object(
+ posix_spawn
+ SRCS
+ posix_spawn.cpp
+ HDRS
+ ../posix_spawn.h
+ DEPENDS
+ libc.include.fcntl
+ libc.include.spawn
+ libc.include.sys_syscall
+ libc.src.__support.CPP.optional
+ libc.src.__support.OSUtil.osutil
+ libc.src.spawn.file_actions
+)
--- /dev/null
+//===-- Linux implementation of posix_spawn -------------------------------===//
+//
+// 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/spawn/posix_spawn.h"
+
+#include "src/__support/CPP/optional.h"
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+#include "src/spawn/file_actions.h"
+
+#include <fcntl.h>
+#include <spawn.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+namespace {
+
+pid_t fork() {
+ // TODO: Use only the clone syscall and use a sperate small stack in the child
+ // to avoid duplicating the complete stack from the parent. A new stack will
+ // be created on exec anyway so duplicating the full stack is unnecessary.
+#ifdef SYS_fork
+ return __llvm_libc::syscall_impl(SYS_fork);
+#elif defined(SYS_clone)
+ return __llvm_libc::syscall_impl(SYS_clone, SIGCHLD, 0);
+#else
+#error "SYS_fork or SYS_clone not available."
+#endif
+}
+
+cpp::optional<int> open(const char *path, int oflags, mode_t mode) {
+#ifdef SYS_open
+ int fd = __llvm_libc::syscall_impl(SYS_open, path, oflags, mode);
+#else
+ int fd = __llvm_libc::syscall_impl(SYS_openat, AT_FDCWD, path, oflags, mode);
+#endif
+ if (fd > 0)
+ return fd;
+ // The open function is called as part of the child process' preparatory
+ // steps. If an open fails, the child process just exits. So, unlike
+ // the public open function, we do not need to set errno here.
+ return cpp::nullopt;
+}
+
+void close(int fd) { __llvm_libc::syscall_impl(SYS_close, fd); }
+
+bool dup2(int fd, int newfd) {
+ long ret = __llvm_libc::syscall_impl(SYS_dup2, fd, newfd);
+ return ret < 0 ? false : true;
+}
+
+// All exits from child_process are error exits. So, we use a simple
+// exit implementation which exits with code 127.
+void exit() {
+ for (;;) {
+ __llvm_libc::syscall_impl(SYS_exit_group, 127);
+ __llvm_libc::syscall_impl(SYS_exit, 127);
+ }
+}
+
+void child_process(const char *__restrict path,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *__restrict, // For now unused
+ char *const *__restrict argv, char *const *__restrict envp) {
+ // TODO: In the code below, the child_process just exits on error during
+ // processing |file_actions| and |attr|. The correct way would be to exit
+ // after conveying the information about the failure to the parent process
+ // (via a pipe for example).
+ // TODO: Handle |attr|.
+
+ if (file_actions != nullptr) {
+ auto *act = reinterpret_cast<BaseSpawnFileAction *>(file_actions->__front);
+ while (act != nullptr) {
+ switch (act->type) {
+ case BaseSpawnFileAction::OPEN: {
+ auto *open_act = reinterpret_cast<SpawnFileOpenAction *>(act);
+ auto fd = open(open_act->path, open_act->oflag, open_act->mode);
+ if (!fd)
+ exit();
+ int actual_fd = *fd;
+ if (actual_fd != open_act->fd) {
+ bool dup2_result = dup2(actual_fd, open_act->fd);
+ close(actual_fd); // The old fd is not needed anymore.
+ if (!dup2_result)
+ exit();
+ }
+ break;
+ }
+ case BaseSpawnFileAction::CLOSE: {
+ auto *close_act = reinterpret_cast<SpawnFileCloseAction *>(act);
+ close(close_act->fd);
+ break;
+ }
+ case BaseSpawnFileAction::DUP2: {
+ auto *dup2_act = reinterpret_cast<SpawnFileDup2Action *>(act);
+ if (!dup2(dup2_act->fd, dup2_act->newfd))
+ exit();
+ break;
+ }
+ }
+ act = act->next;
+ }
+ }
+
+ if (__llvm_libc::syscall_impl(SYS_execve, path, argv, envp) < 0)
+ exit();
+}
+
+} // anonymous namespace
+
+LLVM_LIBC_FUNCTION(int, posix_spawn,
+ (pid_t *__restrict pid, const char *__restrict path,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *__restrict attr,
+ char *const *__restrict argv,
+ char *const *__restrict envp)) {
+ pid_t cpid = fork();
+ if (cpid == 0)
+ child_process(path, file_actions, attr, argv, envp);
+ else if (cpid < 0)
+ return -cpid;
+
+ if (pid != nullptr)
+ *pid = cpid;
+
+ // TODO: Before returning, one should wait for the child_process to startup
+ // successfully. For now, we will just return. Future changes will add proper
+ // wait (using pipes for example).
+
+ return 0;
+}
+
+} // namespace __llvm_libc
--- /dev/null
+//===-- Implementation header for posix_spawn -------------------*- 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_SPAWN_POSIX_SPAWN_H
+#define LLVM_LIBC_SRC_SPAWN_POSIX_SPAWN_H
+
+#include <spawn.h>
+
+namespace __llvm_libc {
+
+int posix_spawn(pid_t *__restrict pid, const char *__restrict path,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *__restrict attr,
+ char *const *__restrict argv, char *const *__restrict envp);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SPAWN_POSIX_SPAWN_H
add_subdirectory(__support)
add_subdirectory(pthread)
+add_subdirectory(spawn)
add_subdirectory(stdio)
add_subdirectory(stdlib)
add_subdirectory(threads)
--- /dev/null
+add_custom_target(spawn-integration-tests)
+add_dependencies(libc-integration-tests spawn-integration-tests)
+
+add_executable(
+ libc_posix_spawn_test_binary
+ EXCLUDE_FROM_ALL
+ posix_spawn_test_binary.cpp
+ test_binary_properties.h
+)
+set_target_properties(
+ libc_posix_spawn_test_binary
+ PROPERTIES
+ OUTPUT_NAME libc_posix_spawn_test_binary
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+add_header_library(
+ test_binary_properties
+ HDRS
+ test_binary_properties.h
+)
+
+add_integration_test(
+ posix_spawn_test
+ SUITE
+ spawn-integration-tests
+ SRCS
+ posix_spawn_test.cpp
+ LOADER
+ libc.loader.linux.crt1
+ DEPENDS
+ libc_posix_spawn_test_binary
+ libc.test.integration.src.spawn.test_binary_properties
+ libc.include.fcntl
+ libc.include.signal
+ libc.include.spawn
+ libc.include.sys_wait
+ libc.src.signal.raise
+ libc.src.spawn.posix_spawn
+ libc.src.spawn.posix_spawn_file_actions_addopen
+ libc.src.spawn.posix_spawn_file_actions_destroy
+ libc.src.spawn.posix_spawn_file_actions_init
+ libc.src.sys.wait.waitpid
+)
+
+add_subdirectory(testdata)
--- /dev/null
+//===-- Unittests for posix_spawn -----------------------------------------===//
+//
+// 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 "test_binary_properties.h"
+
+#include "src/spawn/posix_spawn.h"
+#include "src/spawn/posix_spawn_file_actions_addopen.h"
+#include "src/spawn/posix_spawn_file_actions_destroy.h"
+#include "src/spawn/posix_spawn_file_actions_init.h"
+#include "src/sys/wait/waitpid.h"
+#include "utils/IntegrationTest/test.h"
+
+#include <fcntl.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/wait.h>
+
+char arg0[] = "libc_posix_spawn_test_binary";
+char *argv[] = {
+ arg0,
+ nullptr,
+};
+
+void spawn_and_wait_for_normal_exit(char **envp) {
+ pid_t cpid;
+ posix_spawn_file_actions_t file_actions;
+ ASSERT_EQ(__llvm_libc::posix_spawn_file_actions_init(&file_actions), 0);
+ __llvm_libc::posix_spawn_file_actions_addopen(
+ &file_actions, CHILD_FD, "testdata/posix_spawn.test", O_RDONLY, 0);
+ ASSERT_EQ(
+ __llvm_libc::posix_spawn(&cpid, arg0, &file_actions, nullptr, argv, envp),
+ 0);
+ ASSERT_TRUE(cpid > 0);
+ int status;
+ ASSERT_EQ(__llvm_libc::waitpid(cpid, &status, 0), cpid);
+ ASSERT_EQ(__llvm_libc::posix_spawn_file_actions_destroy(&file_actions), 0);
+ ASSERT_TRUE(WIFEXITED(status));
+ int exit_status = WEXITSTATUS(status);
+ ASSERT_EQ(exit_status, 0);
+}
+
+TEST_MAIN(int argc, char **argv, char **envp) {
+ spawn_and_wait_for_normal_exit(envp);
+ return 0;
+}
--- /dev/null
+#include "test_binary_properties.h"
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+ if (argc != 1)
+ return 5;
+ constexpr size_t bufsize = sizeof(TEXT);
+ char buf[bufsize];
+ ssize_t readsize = bufsize - 1;
+ ssize_t len = read(CHILD_FD, buf, readsize);
+ if (len != readsize) {
+ return 1;
+ }
+ buf[readsize] = '\0'; // Null terminator
+ if (close(CHILD_FD) != 0)
+ return 2;
+ if (strcmp(buf, TEXT) != 0)
+ return 3;
+ return 0;
+}
--- /dev/null
+//===-- Common definitions shared between test binary and test --*- 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 LIBC_TEST_INTEGRATION_SRC_SPAWN_TEST_BINARY_PROPERTIES_H
+#define LIBC_TEST_INTEGRATION_SRC_SPAWN_TEST_BINARY_PROPERTIES_H
+
+constexpr int CHILD_FD = 10;
+constexpr char TEXT[] = "Hello, posix_spawn";
+
+#endif // LIBC_TEST_INTEGRATION_SRC_SPAWN_TEST_BINARY_PROPERTIES_H
--- /dev/null
+file(GENERATE OUTPUT posix_spawn.test CONTENT "Hello, posix_spawn")
-add_header_library(
+add_object_library(
test
+ SRCS
+ test.cpp
HDRS
test.h
DEPENDS
--- /dev/null
+//===-- Simple malloc and free for use with integration tests -------------===//
+//
+// 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 <stddef.h>
+#include <stdint.h>
+
+// Integration tests cannot use the SCUDO standalone allocator as SCUDO pulls
+// various other parts of the libc. Since SCUDO development does not use
+// LLVM libc build rules, it is very hard to keep track or pull all that SCUDO
+// requires. Hence, as a work around for this problem, we use a simple allocator
+// which just hands out continuous blocks from a statically allocated chunk of
+// memory.
+
+static uint8_t memory[16384];
+static uint8_t *ptr = memory;
+
+extern "C" {
+
+void *malloc(size_t s) {
+ void *mem = ptr;
+ ptr += s;
+ return mem;
+}
+
+void free(void *) {}
+
+} // extern "C"