Skip to content

Commit

Permalink
[libc] Add a simple implementation of the posix_spawn function.
Browse files Browse the repository at this point in the history
The implementation currently ignores all spawn attributes. Support for
them will be added in future changes.

A simple allocator for integration tests has been added so that the
integration test for posix_spawn can use the
posix_spawn_file_actions_add* functions.

Reviewed By: michaelrj

Differential Revision: https://reviews.llvm.org/D135752
  • Loading branch information
Siva Chandra Reddy committed Oct 13, 2022
1 parent dde9db5 commit 02a543d
Show file tree
Hide file tree
Showing 20 changed files with 396 additions and 4 deletions.
3 changes: 2 additions & 1 deletion libc/cmake/modules/LLVMLibCTestRules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@ function(add_integration_test test_name)
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.
Expand Down
2 changes: 1 addition & 1 deletion libc/config/linux/api.td
Original file line number Diff line number Diff line change
Expand Up @@ -291,5 +291,5 @@ def SysUtsNameAPI : PublicAPI<"sys/utsname.h"> {
}

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"];
}
1 change: 1 addition & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ if(LLVM_LIBC_FULL_BUILD)
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
Expand Down
2 changes: 2 additions & 0 deletions libc/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ add_gen_header(
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
)

Expand Down
1 change: 1 addition & 0 deletions libc/include/llvm-libc-types/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ add_header(off_t HDR off_t.h)
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)
Expand Down
16 changes: 16 additions & 0 deletions libc/include/llvm-libc-types/posix_spawnattr_t.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//===-- 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
13 changes: 12 additions & 1 deletion libc/spec/posix.td
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,12 @@ def AtForkCallbackT : NamedType<"__atfork_callback_t">;

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>;
Expand Down Expand Up @@ -1052,7 +1056,7 @@ def POSIX : StandardSpec<"POSIX"> {
HeaderSpec Spawn = HeaderSpec<
"spawn.h",
[], // Macros
[PosixSpawnFileActionsT, ModeTType],
[ModeTType, PosixSpawnAttrT, PidT, PosixSpawnFileActionsT],
[], // Enumerations
[
FunctionSpec<
Expand Down Expand Up @@ -1081,6 +1085,13 @@ def POSIX : StandardSpec<"POSIX"> {
RetValSpec<IntType>,
[ArgSpec<PosixSpawnFileActionsTPtr>]
>,
FunctionSpec<
"posix_spawn",
RetValSpec<IntType>,
[ArgSpec<RestrictedPidTPtr>, ArgSpec<ConstCharRestrictedPtr>,
ArgSpec<PosixSpawnFileActionsTPtr>, ArgSpec<RestrictedPosixSpawnAttrTPtrType>,
ArgSpec<ConstCharRestrictedPtrPtr>, ArgSpec<ConstCharRestrictedPtrPtr>]
>,
]
>;

Expand Down
3 changes: 3 additions & 0 deletions libc/spec/spec.td
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def ConstCharPtr : ConstType<CharPtr>;
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>;
Expand Down Expand Up @@ -115,6 +116,8 @@ def FILERestrictedPtr : RestrictedPtrType<FILE>;
def PThreadTType : NamedType<"pthread_t">;

def PidT : NamedType<"pid_t">;
def RestrictedPidTPtr : RestrictedPtrType<PidT>;

def StructRUsage : NamedType<"struct rusage">;
def StructRUsagePtr : PtrType<StructRUsage>;

Expand Down
11 changes: 11 additions & 0 deletions libc/src/spawn/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
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
Expand Down Expand Up @@ -63,3 +67,10 @@ add_entrypoint_object(
libc.include.errno
libc.include.spawn
)

add_entrypoint_object(
posix_spawn
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.posix_spawn
)
14 changes: 14 additions & 0 deletions libc/src/spawn/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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
)
139 changes: 139 additions & 0 deletions libc/src/spawn/linux/posix_spawn.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//===-- 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
23 changes: 23 additions & 0 deletions libc/src/spawn/posix_spawn.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===-- 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
1 change: 1 addition & 0 deletions libc/test/integration/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
add_subdirectory(__support)
add_subdirectory(pthread)
add_subdirectory(spawn)
add_subdirectory(stdio)
add_subdirectory(stdlib)
add_subdirectory(threads)
Expand Down
46 changes: 46 additions & 0 deletions libc/test/integration/src/spawn/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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)
Loading

0 comments on commit 02a543d

Please sign in to comment.