[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