[libc-commits] [libc] [libc] Implement execlp in unistd (PR #232901)

Aman Maurya via libc-commits libc-commits at lists.llvm.org
Tue Oct 20 08:02:40 PDT 2026


https://github.com/amanmaurya92 updated https://github.com/llvm/llvm-project/pull/232901

>From 1e290ab644fbed089878faa5aa28b7d830b33955 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Sat, 17 Oct 2026 00:44:39 +0530
Subject: [PATCH 1/2] [libc] Implement execlp in unistd

Implement the standard POSIX function `execlp` in `<unistd.h>`.

Part of #232374.

Assisted by Antigravity and reviewed by Aman Maurya.
---
 libc/config/linux/aarch64/entrypoints.txt     |  1 +
 libc/config/linux/riscv/entrypoints.txt       |  1 +
 libc/config/linux/x86_64/entrypoints.txt      |  1 +
 libc/include/unistd.yaml                      |  8 ++
 libc/src/unistd/CMakeLists.txt                |  7 ++
 libc/src/unistd/execlp.h                      | 25 +++++++
 libc/src/unistd/linux/CMakeLists.txt          | 14 ++++
 libc/src/unistd/linux/execlp.cpp              | 55 ++++++++++++++
 .../integration/src/unistd/CMakeLists.txt     | 20 +++++
 .../integration/src/unistd/execlp_test.cpp    | 74 +++++++++++++++++++
 libc/test/src/unistd/CMakeLists.txt           | 13 ++++
 libc/test/src/unistd/execlp_test.cpp          | 38 ++++++++++
 12 files changed, 257 insertions(+)
 create mode 100644 libc/src/unistd/execlp.h
 create mode 100644 libc/src/unistd/linux/execlp.cpp
 create mode 100644 libc/test/integration/src/unistd/execlp_test.cpp
 create mode 100644 libc/test/src/unistd/execlp_test.cpp

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 42ce377d7dca9a..04f4e0ebaf9b1a 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1454,6 +1454,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.unistd.confstr
     libc.src.unistd.environ
     libc.src.unistd.execl
+    libc.src.unistd.execlp
     libc.src.unistd.execv
     libc.src.unistd.fork
     libc.src.unistd.getopt
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 7e0511487c2cf5..9e97bb4948df27 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1653,6 +1653,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.unistd.confstr
     libc.src.unistd.environ
     libc.src.unistd.execl
+    libc.src.unistd.execlp
     libc.src.unistd.execv
     libc.src.unistd.fork
     libc.src.unistd.getopt
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 155ae28d753bec..b9443254a9e4e1 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1679,6 +1679,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.unistd.confstr
     libc.src.unistd.environ
     libc.src.unistd.execl
+    libc.src.unistd.execlp
     libc.src.unistd.execv
     libc.src.unistd.fork
     libc.src.unistd.getopt
diff --git a/libc/include/unistd.yaml b/libc/include/unistd.yaml
index 5964e35b6ce5d2..462407707e255e 100644
--- a/libc/include/unistd.yaml
+++ b/libc/include/unistd.yaml
@@ -222,6 +222,14 @@ functions:
       - type: const char *
       - type: const char *
       - type: '...'
+  - name: execlp
+    standards:
+      - posix
+    return_type: int
+    arguments:
+      - type: const char *
+      - type: const char *
+      - type: '...'
   - name: execv
     standards:
       - posix
diff --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt
index 27b74b8ca2de3c..62c637bd5b69f1 100644
--- a/libc/src/unistd/CMakeLists.txt
+++ b/libc/src/unistd/CMakeLists.txt
@@ -161,6 +161,13 @@ add_entrypoint_object(
     .${LIBC_TARGET_OS}.execle
 )
 
+add_entrypoint_object(
+  execlp
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.execlp
+)
+
 add_entrypoint_object(
   execv
   ALIAS
diff --git a/libc/src/unistd/execlp.h b/libc/src/unistd/execlp.h
new file mode 100644
index 00000000000000..3b1c8800484818
--- /dev/null
+++ b/libc/src/unistd/execlp.h
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for execlp.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_UNISTD_EXECLP_H
+#define LLVM_LIBC_SRC_UNISTD_EXECLP_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int execlp(const char *file, const char *arg0, ...);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_UNISTD_EXECLP_H
diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index 61030ec7e12283..2d4f906a299713 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -235,6 +235,20 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
+add_entrypoint_object(
+  execlp
+  SRCS
+    execlp.cpp
+  HDRS
+    ../execlp.h
+  DEPENDS
+    libc.hdr.types.size_t
+    libc.src.__support.common
+    libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
+    libc.src.unistd.execvp
+)
+
 add_entrypoint_object(
   execv
   SRCS
diff --git a/libc/src/unistd/linux/execlp.cpp b/libc/src/unistd/linux/execlp.cpp
new file mode 100644
index 00000000000000..8114cda0857858
--- /dev/null
+++ b/libc/src/unistd/linux/execlp.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of execlp.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/unistd/execlp.h"
+
+#include "hdr/types/size_t.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/unistd/execvp.h"
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, execlp, (const char *file, const char *arg0, ...)) {
+  LIBC_CRASH_ON_NULLPTR(arg0);
+
+  va_list varargs, varargs_copy;
+  va_start(varargs, arg0);
+  va_copy(varargs_copy, varargs);
+
+  size_t argc = 1;
+  while (va_arg(varargs, const char *) != nullptr)
+    ++argc;
+  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;
+
+  return LIBC_NAMESPACE::execvp(file, argv);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/integration/src/unistd/CMakeLists.txt b/libc/test/integration/src/unistd/CMakeLists.txt
index cc04b374a83789..31495c9d2b27e2 100644
--- a/libc/test/integration/src/unistd/CMakeLists.txt
+++ b/libc/test/integration/src/unistd/CMakeLists.txt
@@ -224,4 +224,24 @@ add_integration_test(
     libc.src.unistd.fork
   ENV
     EXECV_TEST=PASS
+
+add_integration_test(
+  execlp_test
+  SUITE
+    unistd-integration-tests
+  SRCS
+    execlp_test.cpp
+  DEPENDS
+    libc_execv_test_normal_exit
+    libc_exec_test_signal_exit
+    libc.hdr.signal_macros
+    libc.hdr.sys_wait_macros
+    libc.src.errno.errno
+    libc.src.sys.wait.waitpid
+    libc.src.unistd.environ
+    libc.src.unistd.execlp
+    libc.src.unistd.fork
+  ENV
+    EXECV_TEST=PASS
+    PATH=.
 )
diff --git a/libc/test/integration/src/unistd/execlp_test.cpp b/libc/test/integration/src/unistd/execlp_test.cpp
new file mode 100644
index 00000000000000..a8888f540298cb
--- /dev/null
+++ b/libc/test/integration/src/unistd/execlp_test.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Integration test for execlp.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/wait/waitpid.h"
+#include "src/unistd/execlp.h"
+#include "src/unistd/fork.h"
+
+#include "hdr/signal_macros.h"
+#include "hdr/sys_wait_macros.h"
+#include "test/IntegrationTest/test.h"
+
+void fork_and_execlp_path_search() {
+  pid_t pid = LIBC_NAMESPACE::fork();
+  if (pid == 0) {
+    LIBC_NAMESPACE::execlp("libc_execv_test_normal_exit",
+                           "libc_execv_test_normal_exit",
+                           static_cast<char *>(nullptr));
+  }
+  ASSERT_TRUE(pid > 0);
+  int status;
+  pid_t cpid = LIBC_NAMESPACE::waitpid(pid, &status, 0);
+  ASSERT_TRUE(cpid > 0);
+  ASSERT_EQ(cpid, pid);
+  ASSERT_TRUE(WIFEXITED(status));
+}
+
+void fork_and_execlp_slash_path() {
+  pid_t pid = LIBC_NAMESPACE::fork();
+  if (pid == 0) {
+    LIBC_NAMESPACE::execlp("./libc_execv_test_normal_exit",
+                           "libc_execv_test_normal_exit",
+                           static_cast<char *>(nullptr));
+  }
+  ASSERT_TRUE(pid > 0);
+  int status;
+  pid_t cpid = LIBC_NAMESPACE::waitpid(pid, &status, 0);
+  ASSERT_TRUE(cpid > 0);
+  ASSERT_EQ(cpid, pid);
+  ASSERT_TRUE(WIFEXITED(status));
+}
+
+void fork_and_execlp_signal_exit() {
+  pid_t pid = LIBC_NAMESPACE::fork();
+  if (pid == 0) {
+    LIBC_NAMESPACE::execlp("libc_exec_test_signal_exit",
+                           "libc_exec_test_signal_exit",
+                           static_cast<char *>(nullptr));
+  }
+  ASSERT_TRUE(pid > 0);
+  int status;
+  pid_t cpid = LIBC_NAMESPACE::waitpid(pid, &status, 0);
+  ASSERT_TRUE(cpid > 0);
+  ASSERT_EQ(cpid, pid);
+  ASSERT_FALSE(WIFEXITED(status));
+  ASSERT_TRUE(WTERMSIG(status) == SIGUSR1);
+}
+
+TEST_MAIN([[maybe_unused]] int argc, [[maybe_unused]] char **argv,
+          [[maybe_unused]] char **envp) {
+  fork_and_execlp_path_search();
+  fork_and_execlp_slash_path();
+  fork_and_execlp_signal_exit();
+  return 0;
+}
diff --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt
index 2e8d9bb8e2509f..0aa7f99a00a85c 100644
--- a/libc/test/src/unistd/CMakeLists.txt
+++ b/libc/test/src/unistd/CMakeLists.txt
@@ -245,6 +245,19 @@ add_libc_test(
     libc.test.UnitTest.ErrnoSetterMatcher
 )
 
+add_libc_test(
+  execlp_test
+  SUITE
+    libc_unistd_unittests
+  SRCS
+    execlp_test.cpp
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.src.unistd.execlp
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
 add_libc_test(
   fexecve_test
   SUITE
diff --git a/libc/test/src/unistd/execlp_test.cpp b/libc/test/src/unistd/execlp_test.cpp
new file mode 100644
index 00000000000000..66c50f69e6fbb1
--- /dev/null
+++ b/libc/test/src/unistd/execlp_test.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for execlp.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "src/unistd/execlp.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcExeclpTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcExeclpTest, NonExistentFile) {
+  using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+  ASSERT_THAT(LIBC_NAMESPACE::execlp("nonexistent_binary_xyz_123",
+                                     "nonexistent_binary_xyz_123", nullptr),
+              Fails(ENOENT));
+}
+
+TEST_F(LlvmLibcExeclpTest, NonExistentPathWithSlash) {
+  using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+  ASSERT_THAT(LIBC_NAMESPACE::execlp("/nonexistent/path/xyz", "xyz", nullptr),
+              Fails(ENOENT));
+}
+
+TEST_F(LlvmLibcExeclpTest, EmptyFileName) {
+  using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+  ASSERT_THAT(LIBC_NAMESPACE::execlp("", "", nullptr), Fails(ENOENT));
+}

>From 33e6fb135b710d439b6ea5ab72f4fb5b3540c2eb Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Sat, 17 Oct 2026 02:23:04 +0530
Subject: [PATCH 2/2] Fix cmake-format in execlp integration test

Assisted by Antigravity and reviewed by Aman Maurya.
---
 libc/test/integration/src/unistd/CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libc/test/integration/src/unistd/CMakeLists.txt b/libc/test/integration/src/unistd/CMakeLists.txt
index 31495c9d2b27e2..d59fa99471650a 100644
--- a/libc/test/integration/src/unistd/CMakeLists.txt
+++ b/libc/test/integration/src/unistd/CMakeLists.txt
@@ -224,6 +224,7 @@ add_integration_test(
     libc.src.unistd.fork
   ENV
     EXECV_TEST=PASS
+)
 
 add_integration_test(
   execlp_test



More information about the libc-commits mailing list