[libc] Add POSIX execv and execve functions.

The POSIX global variable environ has also been added.

Reviewed By: michaelrj

Differential Revision: https://reviews.llvm.org/D135351
This commit is contained in:
Siva Chandra Reddy 2022-10-06 05:24:18 +00:00
parent eaa583c330
commit 3f965818b6
25 changed files with 454 additions and 3 deletions

View File

@ -431,7 +431,8 @@ function(add_integration_test test_name)
APPEND fq_deps_list
libc.src.__support.threads.thread
libc.src.stdlib.atexit
libc.src.stdlib.exit)
libc.src.stdlib.exit
libc.src.unistd.environ)
list(REMOVE_DUPLICATES fq_deps_list)
# We don't want memory functions to be dependencies on integration tests.
@ -533,7 +534,8 @@ function(add_integration_test test_name)
add_dependencies(${fq_target_name}
${fq_target_name}.__copy_loader__
${fq_libc_target_name}
libc.utils.IntegrationTest.test)
libc.utils.IntegrationTest.test
${INTEGRATION_TEST_DEPENDS})
add_custom_command(
TARGET ${fq_target_name}

View File

@ -377,6 +377,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.environ
)
endif()

View File

@ -259,7 +259,7 @@ def DirentAPI : PublicAPI<"dirent.h"> {
}
def UniStdAPI : PublicAPI<"unistd.h"> {
let Types = ["off_t", "pid_t", "size_t", "ssize_t", "uid_t"];
let Types = ["__exec_argv_t", "__exec_envp_t", "off_t", "pid_t", "size_t", "ssize_t", "uid_t"];
}
def SysResourceAPI : PublicAPI<"sys/resource.h"> {

View File

@ -143,6 +143,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.unistd.dup
libc.src.unistd.dup2
libc.src.unistd.dup3
libc.src.unistd.execve
libc.src.unistd.fchdir
libc.src.unistd.fsync
libc.src.unistd.ftruncate
@ -412,6 +413,8 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.time.clock_gettime
# unistd.h entrypoints
libc.src.unistd.environ
libc.src.unistd.execv
libc.src.unistd.fork
libc.src.unistd.__llvm_libc_syscall
)

View File

@ -175,6 +175,8 @@ add_gen_header(
.llvm_libc_common_h
.llvm-libc-macros.file_seek_macros
.llvm-libc-macros.unistd_macros
.llvm-libc-types.__exec_argv_t
.llvm-libc-types.__exec_envp_t
.llvm-libc-types.off_t
.llvm-libc-types.pid_t
.llvm-libc-types.size_t

View File

@ -2,6 +2,8 @@ add_header(off64_t HDR off64_t.h)
add_header(size_t HDR size_t.h)
add_header(__bsearchcompare_t HDR __bsearchcompare_t.h)
add_header(__call_once_func_t HDR __call_once_func_t.h)
add_header(__exec_argv_t HDR __exec_argv_t.h)
add_header(__exec_envp_t HDR __exec_envp_t.h)
add_header(__futex_word HDR __futex_word.h)
add_header(__mutex_type HDR __mutex_type.h DEPENDS .__futex_word)
add_header(__pthread_once_func_t HDR __pthread_once_func_t.h)

View File

@ -0,0 +1,14 @@
//===-- Definition of type __exec_argv_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_EXEC_ARGV_T_H__
#define __LLVM_LIBC_TYPES_EXEC_ARGV_T_H__
typedef char *const __exec_argv_t[];
#endif // __LLVM_LIBC_TYPES_EXEC_ARGV_T_H__

View File

@ -0,0 +1,14 @@
//===-- Definition of type __exec_envp_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_EXEC_ENVP_T_H__
#define __LLVM_LIBC_TYPES_EXEC_ENVP_T_H__
typedef char *const __exec_envp_t[];
#endif // __LLVM_LIBC_TYPES_EXEC_ENVP_T_H__

View File

@ -20,6 +20,7 @@
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
extern "C" int main(int, char **, char **);
@ -147,6 +148,9 @@ __attribute__((noinline)) static void do_start() {
while (*env_end_marker)
++env_end_marker;
// Initialize the POSIX global declared in unistd.h
environ = reinterpret_cast<char **>(env_ptr);
// After the env array, is the aux-vector. The end of the aux-vector is
// denoted by an AT_NULL entry.
Elf64_Phdr *programHdrTable = nullptr;

View File

@ -6,11 +6,13 @@ add_loader_object(
libc.config.linux.app_h
libc.include.sys_mman
libc.include.sys_syscall
libc.include.unistd
libc.src.__support.threads.thread
libc.src.__support.OSUtil.osutil
libc.src.stdlib.exit
libc.src.stdlib.atexit
libc.src.string.memory_utils.memcpy_implementation
libc.src.unistd.environ
COMPILE_OPTIONS
-fno-omit-frame-pointer
-ffreestanding # To avoid compiler warnings about calling the main function.

View File

@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
extern "C" int main(int, char **, char **);
@ -167,6 +168,9 @@ extern "C" void _start() {
while (*env_end_marker)
++env_end_marker;
// Initialize the POSIX global declared in unistd.h
environ = reinterpret_cast<char **>(env_ptr);
// After the env array, is the aux-vector. The end of the aux-vector is
// denoted by an AT_NULL entry.
Elf64_Phdr *programHdrTable = nullptr;

View File

@ -45,6 +45,9 @@ def ConstStructDirentPtrPtr : ConstType<StructDirentPtrPtr>;
def StructTimeSpec : NamedType<"struct timespec">;
def StructTimeSpecPtr : PtrType<StructTimeSpec>;
def ExecArgvT : NamedType<"__exec_argv_t">;
def ExecEnvpT : NamedType<"__exec_envp_t">;
def POSIX : StandardSpec<"POSIX"> {
PtrType CharPtr = PtrType<CharType>;
RestrictedPtrType RestrictedCharPtr = RestrictedPtrType<CharType>;
@ -299,6 +302,8 @@ def POSIX : StandardSpec<"POSIX"> {
"unistd.h",
[], // Macros
[
ExecArgvT,
ExecEnvpT,
OffTType,
SSizeTType,
SizeTType,
@ -342,6 +347,16 @@ def POSIX : StandardSpec<"POSIX"> {
RetValSpec<IntType>,
[ArgSpec<IntType>]
>,
FunctionSpec<
"execv",
RetValSpec<IntType>,
[ArgSpec<ConstCharPtr>, ArgSpec<ExecArgvT>]
>,
FunctionSpec<
"execve",
RetValSpec<IntType>,
[ArgSpec<ConstCharPtr>, ArgSpec<ExecArgvT>, ArgSpec<ExecEnvpT>]
>,
FunctionSpec<
"fork",
RetValSpec<PidT>,
@ -512,6 +527,9 @@ def POSIX : StandardSpec<"POSIX"> {
RetValSpec<SSizeTType>,
[ArgSpec<IntType>, ArgSpec<ConstVoidPtr>, ArgSpec<SizeTType>]
>,
],
[
ObjectSpec<"environ", "char **">,
]
>;

View File

@ -58,6 +58,20 @@ add_entrypoint_object(
.${LIBC_TARGET_OS}.fork
)
add_entrypoint_object(
execv
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.execv
)
add_entrypoint_object(
execve
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.execve
)
add_entrypoint_object(
fsync
ALIAS
@ -211,3 +225,11 @@ add_entrypoint_object(
DEPENDS
.${LIBC_TARGET_OS}.write
)
add_entrypoint_object(
environ
SRCS
environ.cpp
HDRS
environ.h
)

View File

@ -0,0 +1,14 @@
//===-- Declaration of POSIX environ --------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
namespace __llvm_libc {
// This is initialized to the correct value by the statup code.
extern "C" char **environ = nullptr;
} // namespace __llvm_libc

18
libc/src/unistd/environ.h Normal file
View File

@ -0,0 +1,18 @@
//===-- Declaration of POSIX environ ----------------------------*- 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_ENVIRON_H
#define LLVM_LIBC_SRC_UNISTD_ENVIRON_H
namespace __llvm_libc {
extern "C" char **environ;
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_UNISTD_ENVIRON_H

18
libc/src/unistd/execv.h Normal file
View File

@ -0,0 +1,18 @@
//===-- Implementation header for execv -------------------------*- 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_EXECV_H
#define LLVM_LIBC_SRC_UNISTD_EXECV_H
namespace __llvm_libc {
int execv(const char *path, char *const argv[]);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_UNISTD_EXECV_H

18
libc/src/unistd/execve.h Normal file
View File

@ -0,0 +1,18 @@
//===-- Implementation header for execve ------------------------*- 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_EXECVE_H
#define LLVM_LIBC_SRC_UNISTD_EXECVE_H
namespace __llvm_libc {
int execve(const char *path, char *const argv[], char *const envp[]);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_UNISTD_EXECVE_H

View File

@ -105,6 +105,33 @@ add_entrypoint_object(
libc.src.errno.errno
)
add_entrypoint_object(
execv
SRCS
execv.cpp
HDRS
../execv.h
DEPENDS
libc.include.errno
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
libc.src.unistd.environ
)
add_entrypoint_object(
execve
SRCS
execve.cpp
HDRS
../execve.h
DEPENDS
libc.include.errno
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)
add_entrypoint_object(
fsync
SRCS

View File

@ -0,0 +1,33 @@
//===-- Linux implementation of 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/unistd/execv.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, execv, (const char *path, char *const argv[])) {
long ret =
__llvm_libc::syscall_impl(SYS_execve, path, argv, __llvm_libc::environ);
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

View File

@ -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

View File

@ -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
)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}