[libc-commits] [libc] [libc] Deduplicate va_list to argv extraction using exec_with_arg_list (PR #234581)
Aman Maurya via libc-commits
libc-commits at lists.llvm.org
Thu Oct 22 04:15:38 PDT 2026
https://github.com/amanmaurya92 created https://github.com/llvm/llvm-project/pull/234581
Deduplicate the `va_list` to `argv` VLA extraction logic across `execl`, `execle`, and `execlp` into a shared `internal::exec_with_arg_list` helper in `libc/src/unistd/linux/exec_utils.h`.
Follow-up to #232901 suggested by @labath.
>From d523f7a294e9529f7b8a8749301982d6ff43a366 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Thu, 22 Oct 2026 16:36:14 +0530
Subject: [PATCH] [libc] Deduplicate va_list to argv extraction using
exec_with_arg_list
---
libc/src/unistd/linux/CMakeLists.txt | 8 +++---
libc/src/unistd/linux/exec_utils.h | 38 ++++++++++++++++++++++++++++
libc/src/unistd/linux/execl.cpp | 33 +++++-------------------
libc/src/unistd/linux/execle.cpp | 32 +++++------------------
libc/src/unistd/linux/execlp.cpp | 30 +++++-----------------
5 files changed, 61 insertions(+), 80 deletions(-)
diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index 96ab1084ca2b43..51858dc0903795 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -209,14 +209,13 @@ add_entrypoint_object(
HDRS
../execl.h
DEPENDS
- libc.hdr.types.size_t
libc.src.__support.common
libc.src.__support.libc_errno
libc.src.__support.macros.config
- libc.src.__support.macros.null_check
libc.src.__support.OSUtil.linux.syscall_wrappers.execve
libc.src.errno.errno
libc.src.unistd.environ
+ libc.src.unistd.linux.exec_utils
)
add_entrypoint_object(
@@ -226,13 +225,12 @@ add_entrypoint_object(
HDRS
../execle.h
DEPENDS
- libc.hdr.types.size_t
libc.src.__support.common
libc.src.__support.libc_errno
libc.src.__support.macros.config
- libc.src.__support.macros.null_check
libc.src.__support.OSUtil.linux.syscall_wrappers.execve
libc.src.errno.errno
+ libc.src.unistd.linux.exec_utils
)
add_entrypoint_object(
@@ -242,7 +240,6 @@ add_entrypoint_object(
HDRS
../execlp.h
DEPENDS
- libc.hdr.types.size_t
libc.src.__support.common
libc.src.__support.libc_errno
libc.src.__support.macros.config
@@ -293,6 +290,7 @@ add_header_library(
libc.src.__support.CPP.string_view
libc.src.__support.error_or
libc.src.__support.macros.config
+ libc.src.__support.macros.null_check
libc.src.__support.OSUtil.linux.syscall_wrappers.execve
libc.src.stdlib.environ_internal
libc.src.string.memory_utils.inline_memcpy
diff --git a/libc/src/unistd/linux/exec_utils.h b/libc/src/unistd/linux/exec_utils.h
index 88ff3350b1796c..c2b1889894c457 100644
--- a/libc/src/unistd/linux/exec_utils.h
+++ b/libc/src/unistd/linux/exec_utils.h
@@ -22,12 +22,50 @@
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
#include "src/stdlib/environ_internal.h"
#include "src/string/memory_utils/inline_memcpy.h"
+#include <stdarg.h>
+
namespace LIBC_NAMESPACE_DECL {
namespace internal {
+// Extracts argv from a variable argument list and calls exec(argv).
+//
+// The varargs list in `*args` is consumed up to and including the terminating
+// null pointer. If additional arguments follow (such as envp in execle),
+// they can be read from `*args` by the `exec` callback.
+template <typename ExecFn>
+LIBC_INLINE ErrorOr<int> exec_with_arg_list(const char *arg0, va_list *args,
+ ExecFn exec) {
+ LIBC_CRASH_ON_NULLPTR(arg0);
+
+ va_list args_count;
+ va_copy(args_count, *args);
+ size_t argc = 1;
+ while (va_arg(args_count, const char *) != nullptr)
+ ++argc;
+ va_end(args_count);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wvla"
+#if __has_warning("-Wvla-cxx-extension")
+#pragma GCC diagnostic ignored "-Wvla-cxx-extension"
+#endif
+ char *argv[argc + 1];
+#pragma GCC diagnostic pop
+ argv[0] = const_cast<char *>(arg0);
+
+ for (size_t i = 1; i < argc; ++i)
+ argv[i] = va_arg(*args, char *);
+ // Consume the terminating nullptr from *args.
+ va_arg(*args, char *);
+ argv[argc] = nullptr;
+
+ return exec(argv);
+}
+
// TODO: POSIX requires that if execve fails with ENOEXEC, the file is executed
// using the system shell (as if by `sh -- file args...`). The kernel only
// handles ELF, "#!" scripts, and binfmt_misc formats, so shebang-less scripts
diff --git a/libc/src/unistd/linux/execl.cpp b/libc/src/unistd/linux/execl.cpp
index 6eb1a7dcbaed49..a7f149c8c33333 100644
--- a/libc/src/unistd/linux/execl.cpp
+++ b/libc/src/unistd/linux/execl.cpp
@@ -13,46 +13,27 @@
#include "src/unistd/execl.h"
-#include "hdr/types/size_t.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/execve.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
-#include "src/__support/macros/null_check.h"
#include "src/unistd/environ.h"
+#include "src/unistd/linux/exec_utils.h"
#include <stdarg.h>
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, execl, (const char *path, const char *arg0, ...)) {
- LIBC_CRASH_ON_NULLPTR(arg0);
-
- va_list varargs, varargs_copy;
+ va_list varargs;
va_start(varargs, arg0);
- va_copy(varargs_copy, varargs);
-
- size_t argc = 1;
- while (va_arg(varargs, const char *) != nullptr)
- ++argc;
+ auto ret =
+ internal::exec_with_arg_list(arg0, &varargs, [&](char *const argv[]) {
+ return linux_syscalls::execve(
+ path, argv, const_cast<char *const *>(LIBC_NAMESPACE::environ));
+ });
va_end(varargs);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wvla"
-#if __has_warning("-Wvla-cxx-extension")
-#pragma GCC diagnostic ignored "-Wvla-cxx-extension"
-#endif
- char *argv[argc + 1];
-#pragma GCC diagnostic pop
- argv[0] = const_cast<char *>(arg0);
-
- for (size_t i = 1; i < argc; ++i)
- argv[i] = va_arg(varargs_copy, char *);
- va_end(varargs_copy);
- argv[argc] = nullptr;
-
- auto ret = linux_syscalls::execve(
- path, argv, const_cast<char *const *>(LIBC_NAMESPACE::environ));
if (!ret) {
libc_errno = ret.error();
return -1;
diff --git a/libc/src/unistd/linux/execle.cpp b/libc/src/unistd/linux/execle.cpp
index 5167bfc0fd128e..1cb5f3a75c8094 100644
--- a/libc/src/unistd/linux/execle.cpp
+++ b/libc/src/unistd/linux/execle.cpp
@@ -13,44 +13,26 @@
#include "src/unistd/execle.h"
-#include "hdr/types/size_t.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/execve.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
-#include "src/__support/macros/null_check.h"
+#include "src/unistd/linux/exec_utils.h"
#include <stdarg.h>
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, execle, (const char *path, const char *arg0, ...)) {
- LIBC_CRASH_ON_NULLPTR(arg0);
-
- va_list varargs, varargs_copy;
+ va_list varargs;
va_start(varargs, arg0);
- va_copy(varargs_copy, varargs);
-
- size_t argc = 1;
- while (va_arg(varargs, const char *) != nullptr)
- ++argc;
+ auto ret =
+ internal::exec_with_arg_list(arg0, &varargs, [&](char *const argv[]) {
+ char *const *envp = va_arg(varargs, char *const *);
+ return linux_syscalls::execve(path, argv, envp);
+ });
va_end(varargs);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wvla"
-#if __has_warning("-Wvla-cxx-extension")
-#pragma GCC diagnostic ignored "-Wvla-cxx-extension"
-#endif
- char *argv[argc + 1];
-#pragma GCC diagnostic pop
- argv[0] = const_cast<char *>(arg0);
-
- for (size_t i = 1; i <= argc; ++i)
- argv[i] = va_arg(varargs_copy, char *);
- char **envp = va_arg(varargs_copy, char **);
- va_end(varargs_copy);
-
- auto ret = linux_syscalls::execve(path, argv, envp);
if (!ret) {
libc_errno = ret.error();
return -1;
diff --git a/libc/src/unistd/linux/execlp.cpp b/libc/src/unistd/linux/execlp.cpp
index 25882a4dabb292..507fa063979da8 100644
--- a/libc/src/unistd/linux/execlp.cpp
+++ b/libc/src/unistd/linux/execlp.cpp
@@ -13,7 +13,6 @@
#include "src/unistd/execlp.h"
-#include "hdr/types/size_t.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
@@ -27,33 +26,16 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, execlp, (const char *file, const char *arg0, ...)) {
LIBC_CRASH_ON_NULLPTR(file);
- LIBC_CRASH_ON_NULLPTR(arg0);
- va_list varargs, varargs_copy;
+ va_list varargs;
va_start(varargs, arg0);
- va_copy(varargs_copy, varargs);
-
- size_t argc = 1;
- while (va_arg(varargs, const char *) != nullptr)
- ++argc;
+ auto ret =
+ internal::exec_with_arg_list(arg0, &varargs, [&](char *const argv[]) {
+ return internal::execvpe(
+ file, argv, const_cast<char *const *>(LIBC_NAMESPACE::environ));
+ });
va_end(varargs);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wvla"
-#if __has_warning("-Wvla-cxx-extension")
-#pragma GCC diagnostic ignored "-Wvla-cxx-extension"
-#endif
- char *argv[argc + 1];
-#pragma GCC diagnostic pop
- argv[0] = const_cast<char *>(arg0);
-
- for (size_t i = 1; i < argc; ++i)
- argv[i] = va_arg(varargs_copy, char *);
- va_end(varargs_copy);
- argv[argc] = nullptr;
-
- auto ret = internal::execvpe(
- file, argv, const_cast<char *const *>(LIBC_NAMESPACE::environ));
if (!ret) {
libc_errno = ret.error();
return -1;
More information about the libc-commits
mailing list