[llvm-branch-commits] [libc] 9bded32 - [libc] Deduplicate va_list to argv extraction using exec_with_arg_list (#234581)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Oct 22 09:47:55 PDT 2026


Author: Aman Maurya
Date: 2026-10-22T21:21:36+05:30
New Revision: 9bded32fa91eaec4598279bf4da33f1bdc66864a

URL: https://github.com/llvm/llvm-project/commit/9bded32fa91eaec4598279bf4da33f1bdc66864a
DIFF: https://github.com/llvm/llvm-project/commit/9bded32fa91eaec4598279bf4da33f1bdc66864a.diff

LOG: [libc] Deduplicate va_list to argv extraction using exec_with_arg_list (#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.

Added: 
    

Modified: 
    libc/src/unistd/linux/CMakeLists.txt
    libc/src/unistd/linux/exec_utils.h
    libc/src/unistd/linux/execl.cpp
    libc/src/unistd/linux/execle.cpp
    libc/src/unistd/linux/execlp.cpp

Removed: 
    


################################################################################
diff  --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index 96ab1084ca2b4..ad23e55630e75 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -209,7 +209,6 @@ 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
@@ -217,6 +216,7 @@ add_entrypoint_object(
     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 +226,13 @@ 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 +242,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

diff  --git a/libc/src/unistd/linux/exec_utils.h b/libc/src/unistd/linux/exec_utils.h
index 88ff3350b1796..8260027a1a1d0 100644
--- a/libc/src/unistd/linux/exec_utils.h
+++ b/libc/src/unistd/linux/exec_utils.h
@@ -25,9 +25,44 @@
 #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) {
+  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 6eb1a7dcbaed4..b4f57e2695726 100644
--- a/libc/src/unistd/linux/execl.cpp
+++ b/libc/src/unistd/linux/execl.cpp
@@ -13,13 +13,13 @@
 
 #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>
 
@@ -28,31 +28,15 @@ 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 5167bfc0fd128..02ac821aa8983 100644
--- a/libc/src/unistd/linux/execle.cpp
+++ b/libc/src/unistd/linux/execle.cpp
@@ -13,12 +13,12 @@
 
 #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>
 
@@ -27,30 +27,15 @@ 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 25882a4dabb29..a0511f56d2f05 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"
@@ -29,31 +28,15 @@ 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 llvm-branch-commits mailing list