33 changes: 33 additions & 0 deletions libc/src/unistd/linux/execve.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===-- Linux implementation of execve ------------------------------------===//
//
// 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/execve.h"
#include "src/unistd/environ.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.

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, execve,
(const char *path, char *const argv[], char *const envp[])) {
long ret = __llvm_libc::syscall_impl(SYS_execve, path, argv, envp);
if (ret < 0) {
errno = -ret;
return -1;
}

// Control will not reach here on success but have a return statement will
// keep the compilers happy.
return ret;
}

} // namespace __llvm_libc
62 changes: 62 additions & 0 deletions libc/test/integration/src/unistd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,65 @@ add_integration_test(
libc.src.sys.wait.waitpid
libc.src.unistd.fork
)

add_executable(
libc_execv_test_normal_exit
EXCLUDE_FROM_ALL
execv_test_normal_exit.cpp
)
set_target_properties(
libc_execv_test_normal_exit
PROPERTIES
OUTPUT_NAME libc_execv_test_normal_exit
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

add_executable(
libc_execv_test_signal_exit
EXCLUDE_FROM_ALL
execv_test_signal_exit.cpp
)
set_target_properties(
libc_execv_test_signal_exit
PROPERTIES
OUTPUT_NAME libc_execv_test_signal_exit
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

add_integration_test(
execv_test
SUITE
unistd-integration-tests
SRCS
execv_test.cpp
LOADER
libc.loader.linux.crt1
DEPENDS
libc_execv_test_normal_exit
libc_execv_test_signal_exit
libc.include.errno
libc.src.sys.wait.waitpid
libc.src.unistd.execv
libc.src.unistd.fork
ENV
EXECV_TEST=PASS
)

add_integration_test(
execve_test
SUITE
unistd-integration-tests
SRCS
execve_test.cpp
LOADER
libc.loader.linux.crt1
DEPENDS
libc_execv_test_normal_exit
libc_execv_test_signal_exit
libc.include.errno
libc.src.sys.wait.waitpid
libc.src.unistd.execve
libc.src.unistd.fork
ENV
EXECV_TEST=PASS
)
59 changes: 59 additions & 0 deletions libc/test/integration/src/unistd/execv_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//===-- Unittests for execv -----------------------------------------------===//
//
// 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/waitpid.h"
#include "src/unistd/execv.h"
#include "src/unistd/fork.h"

#include "utils/IntegrationTest/test.h"

#include <signal.h>
#include <sys/wait.h>

void fork_and_execv_normal_exit() {
pid_t pid = __llvm_libc::fork();
if (pid == 0) {
const char *path = "libc_execv_test_normal_exit";
char *const argv[] = {
const_cast<char *>("execv_test_normal_exit"),
nullptr,
};
__llvm_libc::execv(path, argv);
}
ASSERT_TRUE(pid > 0);
int status;
pid_t cpid = __llvm_libc::waitpid(pid, &status, 0);
ASSERT_TRUE(cpid > 0);
ASSERT_EQ(cpid, pid);
ASSERT_TRUE(WIFEXITED(status));
}

void fork_and_execv_signal_exit() {
pid_t pid = __llvm_libc::fork();
if (pid == 0) {
const char *path = "libc_execv_test_signal_exit";
char *const argv[] = {
const_cast<char *>("execv_test_normal_exit"),
nullptr,
};
__llvm_libc::execv(path, argv);
}
ASSERT_TRUE(pid > 0);
int status;
pid_t cpid = __llvm_libc::waitpid(pid, &status, 0);
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_execv_normal_exit();
fork_and_execv_signal_exit();
return 0;
}
10 changes: 10 additions & 0 deletions libc/test/integration/src/unistd/execv_test_normal_exit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
char *env = getenv("EXECV_TEST");
if (env == nullptr)
raise(SIGUSR1);
return 0;
}
10 changes: 10 additions & 0 deletions libc/test/integration/src/unistd/execv_test_signal_exit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
char *env = getenv("__MISSING_ENV_VAR__");
if (env == nullptr)
raise(SIGUSR1);
return 0;
}
59 changes: 59 additions & 0 deletions libc/test/integration/src/unistd/execve_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//===-- Unittests for execve ----------------------------------------------===//
//
// 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/waitpid.h"
#include "src/unistd/execve.h"
#include "src/unistd/fork.h"

#include "utils/IntegrationTest/test.h"

#include <signal.h>
#include <sys/wait.h>

void fork_and_execv_normal_exit(char **envp) {
pid_t pid = __llvm_libc::fork();
if (pid == 0) {
const char *path = "libc_execv_test_normal_exit";
char *const argv[] = {
const_cast<char *>("execv_test_normal_exit"),
nullptr,
};
__llvm_libc::execve(path, argv, envp);
}
ASSERT_TRUE(pid > 0);
int status;
pid_t cpid = __llvm_libc::waitpid(pid, &status, 0);
ASSERT_TRUE(cpid > 0);
ASSERT_EQ(cpid, pid);
ASSERT_TRUE(WIFEXITED(status));
}

void fork_and_execv_signal_exit(char **envp) {
pid_t pid = __llvm_libc::fork();
if (pid == 0) {
const char *path = "libc_execv_test_signal_exit";
char *const argv[] = {
const_cast<char *>("execv_test_normal_exit"),
nullptr,
};
__llvm_libc::execve(path, argv, envp);
}
ASSERT_TRUE(pid > 0);
int status;
pid_t cpid = __llvm_libc::waitpid(pid, &status, 0);
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_execv_normal_exit(envp);
fork_and_execv_signal_exit(envp);
return 0;
}