[libc-commits] [libc] Reland libgen (PR #205352)
Jeff Bailey via libc-commits
libc-commits at lists.llvm.org
Tue Jun 23 07:11:59 PDT 2026
https://github.com/kaladron created https://github.com/llvm/llvm-project/pull/205352
This relands the basename / dirname commit with a fix that allows death tests to detect ASan failures.
>From 95499a28a294b080280903cc3b846fb5a28687a5 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Tue, 23 Jun 2026 13:54:31 +0100
Subject: [PATCH 1/2] Reapply "[libc] Implement basename and dirname in
libgen.h (#204554)" (#204856)
This reverts commit ae60782c82682d20764f888321efb5d53df18a4a.
---
libc/config/linux/aarch64/entrypoints.txt | 4 ++
libc/config/linux/arm/entrypoints.txt | 4 ++
libc/config/linux/riscv/entrypoints.txt | 4 ++
libc/config/linux/x86_64/entrypoints.txt | 4 ++
libc/docs/CMakeLists.txt | 1 +
libc/docs/headers/index.rst | 1 +
libc/include/CMakeLists.txt | 8 +++
libc/include/libgen.yaml | 20 ++++++
libc/src/CMakeLists.txt | 1 +
libc/src/__support/CPP/string_view.h | 9 +++
libc/src/libgen/CMakeLists.txt | 23 ++++++
libc/src/libgen/basename.cpp | 42 +++++++++++
libc/src/libgen/basename.h | 30 ++++++++
libc/src/libgen/dirname.cpp | 48 +++++++++++++
libc/src/libgen/dirname.h | 30 ++++++++
libc/test/src/CMakeLists.txt | 1 +
libc/test/src/libgen/CMakeLists.txt | 43 ++++++++++++
libc/test/src/libgen/basename_death_test.cpp | 22 ++++++
libc/test/src/libgen/basename_test.cpp | 62 ++++++++++++++++
libc/test/src/libgen/dirname_death_test.cpp | 22 ++++++
libc/test/src/libgen/dirname_test.cpp | 74 ++++++++++++++++++++
libc/utils/docgen/libgen.yaml | 5 ++
22 files changed, 458 insertions(+)
create mode 100644 libc/include/libgen.yaml
create mode 100644 libc/src/libgen/CMakeLists.txt
create mode 100644 libc/src/libgen/basename.cpp
create mode 100644 libc/src/libgen/basename.h
create mode 100644 libc/src/libgen/dirname.cpp
create mode 100644 libc/src/libgen/dirname.h
create mode 100644 libc/test/src/libgen/CMakeLists.txt
create mode 100644 libc/test/src/libgen/basename_death_test.cpp
create mode 100644 libc/test/src/libgen/basename_test.cpp
create mode 100644 libc/test/src/libgen/dirname_death_test.cpp
create mode 100644 libc/test/src/libgen/dirname_test.cpp
create mode 100644 libc/utils/docgen/libgen.yaml
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 5cddf3dc89799..3072c3d22aa5f 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -109,6 +109,10 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.inttypes.strtoimax
libc.src.inttypes.strtoumax
+ # libgen.h entrypoints
+ libc.src.libgen.basename
+ libc.src.libgen.dirname
+
# stdbit.h entrypoints
libc.src.stdbit.stdc_bit_ceil_uc
libc.src.stdbit.stdc_bit_ceil_ui
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index c4ac53c4925a3..805738a3a5756 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -73,6 +73,10 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.inttypes.strtoimax
libc.src.inttypes.strtoumax
+ # libgen.h entrypoints
+ libc.src.libgen.basename
+ libc.src.libgen.dirname
+
# stdbit.h entrypoints
libc.src.stdbit.stdc_bit_ceil_uc
libc.src.stdbit.stdc_bit_ceil_ui
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index a57efbb8e464d..bcdcf2320f7bd 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -109,6 +109,10 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.inttypes.strtoimax
libc.src.inttypes.strtoumax
+ # libgen.h entrypoints
+ libc.src.libgen.basename
+ libc.src.libgen.dirname
+
# stdbit.h entrypoints
libc.src.stdbit.stdc_bit_ceil_uc
libc.src.stdbit.stdc_bit_ceil_ui
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index ce88a6749d9dc..f19da5902bba1 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -125,6 +125,10 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.inttypes.wcstoimax
libc.src.inttypes.wcstoumax
+ # libgen.h entrypoints
+ libc.src.libgen.basename
+ libc.src.libgen.dirname
+
# stdbit.h entrypoints
libc.src.stdbit.stdc_bit_ceil_uc
libc.src.stdbit.stdc_bit_ceil_ui
diff --git a/libc/docs/CMakeLists.txt b/libc/docs/CMakeLists.txt
index cf54edeae66de..ded99393f9390 100644
--- a/libc/docs/CMakeLists.txt
+++ b/libc/docs/CMakeLists.txt
@@ -54,6 +54,7 @@ if (SPHINX_FOUND)
float
glob
inttypes
+ libgen
locale
nl_types
net/if
diff --git a/libc/docs/headers/index.rst b/libc/docs/headers/index.rst
index e818e1549c0d1..893ddf839cccd 100644
--- a/libc/docs/headers/index.rst
+++ b/libc/docs/headers/index.rst
@@ -20,6 +20,7 @@ Implementation Status
float
glob
inttypes
+ libgen
locale
math/index.rst
net/if
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 549dbd9e4c3f8..e8168687109b0 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -130,6 +130,14 @@ add_header_macro(
.llvm-libc-macros.float_macros
)
+add_header_macro(
+ libgen
+ ../libc/include/libgen.yaml
+ libgen.h
+ DEPENDS
+ .llvm_libc_common_h
+)
+
add_header_macro(
limits
../libc/include/limits.yaml
diff --git a/libc/include/libgen.yaml b/libc/include/libgen.yaml
new file mode 100644
index 0000000000000..c79ab79259be6
--- /dev/null
+++ b/libc/include/libgen.yaml
@@ -0,0 +1,20 @@
+header: libgen.h
+standards:
+ - posix
+macros: []
+types: []
+enums: []
+objects: []
+functions:
+ - name: basename
+ standards:
+ - posix
+ return_type: char *
+ arguments:
+ - type: char *
+ - name: dirname
+ standards:
+ - posix
+ return_type: char *
+ arguments:
+ - type: char *
diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index 9db314f54723b..56085c9632f59 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -7,6 +7,7 @@ add_subdirectory(dlfcn)
add_subdirectory(errno)
add_subdirectory(fenv)
add_subdirectory(inttypes)
+add_subdirectory(libgen)
add_subdirectory(link)
add_subdirectory(math)
add_subdirectory(netinet)
diff --git a/libc/src/__support/CPP/string_view.h b/libc/src/__support/CPP/string_view.h
index 6991fd46a4ace..7b98b7e5fb6c8 100644
--- a/libc/src/__support/CPP/string_view.h
+++ b/libc/src/__support/CPP/string_view.h
@@ -205,6 +205,15 @@ class string_view {
return npos;
}
+ LIBC_INLINE constexpr size_t find_last_not_of(const char c,
+ size_t end = npos) const {
+ end = end >= size() ? size() : end + 1;
+ for (; end > 0; --end)
+ if ((*this)[end - 1] != c)
+ return end - 1;
+ return npos;
+ }
+
// Finds the first character not equal to c in this view, starting at
// position From.
LIBC_INLINE constexpr size_t find_first_not_of(const char c,
diff --git a/libc/src/libgen/CMakeLists.txt b/libc/src/libgen/CMakeLists.txt
new file mode 100644
index 0000000000000..9c315a4e7b41d
--- /dev/null
+++ b/libc/src/libgen/CMakeLists.txt
@@ -0,0 +1,23 @@
+add_entrypoint_object(
+ basename
+ SRCS
+ basename.cpp
+ HDRS
+ basename.h
+ DEPENDS
+ libc.src.__support.CPP.string_view
+ libc.src.__support.common
+ libc.src.__support.macros.config
+)
+
+add_entrypoint_object(
+ dirname
+ SRCS
+ dirname.cpp
+ HDRS
+ dirname.h
+ DEPENDS
+ libc.src.__support.CPP.string_view
+ libc.src.__support.common
+ libc.src.__support.macros.config
+)
diff --git a/libc/src/libgen/basename.cpp b/libc/src/libgen/basename.cpp
new file mode 100644
index 0000000000000..acf53f2d446e9
--- /dev/null
+++ b/libc/src/libgen/basename.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 of basename.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/basename.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(char *, basename, (char *path)) {
+ if (path == nullptr || path[0] == '\0')
+ return const_cast<char *>(".");
+
+ cpp::string_view sv(path);
+ size_t last_non_slash = sv.find_last_not_of('/');
+
+ if (last_non_slash == cpp::string_view::npos)
+ return const_cast<char *>("/");
+
+ size_t last_slash = sv.substr(0, last_non_slash).find_last_of('/');
+
+ size_t start = (last_slash == cpp::string_view::npos) ? 0 : last_slash + 1;
+ size_t end = last_non_slash + 1;
+
+ if (end < sv.size())
+ path[end] = '\0';
+
+ return path + start;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/libgen/basename.h b/libc/src/libgen/basename.h
new file mode 100644
index 0000000000000..15239888aa97a
--- /dev/null
+++ b/libc/src/libgen/basename.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Header for basename.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_LIBGEN_BASENAME_H
+#define LLVM_LIBC_SRC_LIBGEN_BASENAME_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+/// Return the last component of a pathname.
+///
+/// \param path Pointer to the null-terminated pathname string.
+/// \return Pointer to the last component of path, or "." if path is null or
+/// empty, or "/" if path is all slashes.
+char *basename(char *path);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_LIBGEN_BASENAME_H
diff --git a/libc/src/libgen/dirname.cpp b/libc/src/libgen/dirname.cpp
new file mode 100644
index 0000000000000..9dd958b63ce9f
--- /dev/null
+++ b/libc/src/libgen/dirname.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 of dirname.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/dirname.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(char *, dirname, (char *path)) {
+ if (path == nullptr || path[0] == '\0')
+ return const_cast<char *>(".");
+
+ cpp::string_view sv(path);
+ size_t last_non_slash = sv.find_last_not_of('/');
+
+ if (last_non_slash == cpp::string_view::npos)
+ return const_cast<char *>("/");
+
+ size_t last_slash = sv.substr(0, last_non_slash).find_last_of('/');
+
+ if (last_slash == cpp::string_view::npos)
+ return const_cast<char *>(".");
+
+ cpp::string_view dir_sv = sv.substr(0, last_slash);
+ size_t dir_last_non_slash = dir_sv.find_last_not_of('/');
+
+ if (dir_last_non_slash == cpp::string_view::npos) {
+ path[1] = '\0';
+ return path;
+ }
+
+ path[dir_last_non_slash + 1] = '\0';
+ return path;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/libgen/dirname.h b/libc/src/libgen/dirname.h
new file mode 100644
index 0000000000000..4909b2eb222ad
--- /dev/null
+++ b/libc/src/libgen/dirname.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Header for dirname.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_LIBGEN_DIRNAME_H
+#define LLVM_LIBC_SRC_LIBGEN_DIRNAME_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+/// Return the directory component of a pathname.
+///
+/// \param path Pointer to the null-terminated pathname string.
+/// \return Pointer to the directory component of path, or "." if path is null
+/// or empty, or "/" if path is all slashes.
+char *dirname(char *path);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_LIBGEN_DIRNAME_H
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index dd232b29a7a7b..45815c9bba8ca 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -63,6 +63,7 @@ add_subdirectory(complex)
add_subdirectory(ctype)
add_subdirectory(errno)
add_subdirectory(fenv)
+add_subdirectory(libgen)
add_subdirectory(link)
add_subdirectory(math)
add_subdirectory(netinet)
diff --git a/libc/test/src/libgen/CMakeLists.txt b/libc/test/src/libgen/CMakeLists.txt
new file mode 100644
index 0000000000000..d3ee13a9f1cab
--- /dev/null
+++ b/libc/test/src/libgen/CMakeLists.txt
@@ -0,0 +1,43 @@
+add_custom_target(libc-libgen-tests)
+
+add_libc_test(
+ basename_test
+ SUITE
+ libc-libgen-tests
+ SRCS
+ basename_test.cpp
+ DEPENDS
+ libc.src.libgen.basename
+)
+
+add_libc_test(
+ dirname_test
+ SUITE
+ libc-libgen-tests
+ SRCS
+ dirname_test.cpp
+ DEPENDS
+ libc.src.libgen.dirname
+)
+
+add_libc_test(
+ basename_death_test
+ UNIT_TEST_ONLY
+ SUITE
+ libc-libgen-tests
+ SRCS
+ basename_death_test.cpp
+ DEPENDS
+ libc.src.libgen.basename
+)
+
+add_libc_test(
+ dirname_death_test
+ UNIT_TEST_ONLY
+ SUITE
+ libc-libgen-tests
+ SRCS
+ dirname_death_test.cpp
+ DEPENDS
+ libc.src.libgen.dirname
+)
diff --git a/libc/test/src/libgen/basename_death_test.cpp b/libc/test/src/libgen/basename_death_test.cpp
new file mode 100644
index 0000000000000..24100dc48aa1b
--- /dev/null
+++ b/libc/test/src/libgen/basename_death_test.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Death tests for basename.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/basename.h"
+#include "test/UnitTest/Test.h"
+
+#ifdef ENABLE_SUBPROCESS_TESTS
+TEST(LlvmLibcBasenameTest, ModifyReturnValue) {
+ char *r = LIBC_NAMESPACE::basename(nullptr);
+ ASSERT_DEATH([r]() { r[0] = 'a'; }, WITH_SIGNAL(-1));
+}
+#endif
diff --git a/libc/test/src/libgen/basename_test.cpp b/libc/test/src/libgen/basename_test.cpp
new file mode 100644
index 0000000000000..2e8feef715137
--- /dev/null
+++ b/libc/test/src/libgen/basename_test.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 basename.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/basename.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcBasenameTest, NullPointer) {
+ ASSERT_STREQ(LIBC_NAMESPACE::basename(nullptr), ".");
+}
+
+TEST(LlvmLibcBasenameTest, EmptyString) {
+ char path[] = "";
+ ASSERT_STREQ(LIBC_NAMESPACE::basename(path), ".");
+}
+
+TEST(LlvmLibcBasenameTest, RegularPath) {
+ char path[] = "/usr/lib";
+ ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "lib");
+}
+
+TEST(LlvmLibcBasenameTest, TrailingSlash) {
+ char path[] = "/usr/";
+ ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "usr");
+ ASSERT_STREQ(path, "/usr");
+}
+
+TEST(LlvmLibcBasenameTest, SingleSlash) {
+ char path[] = "/";
+ ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "/");
+}
+
+TEST(LlvmLibcBasenameTest, MultipleSlashes) {
+ char path[] = "///";
+ ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "/");
+}
+
+TEST(LlvmLibcBasenameTest, SimpleName) {
+ char path[] = "a";
+ ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "a");
+}
+
+TEST(LlvmLibcBasenameTest, SimpleNameTrailingSlash) {
+ char path[] = "a/";
+ ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "a");
+ ASSERT_STREQ(path, "a");
+}
+
+TEST(LlvmLibcBasenameTest, ComplexPath) {
+ char path[] = "///a///";
+ ASSERT_STREQ(LIBC_NAMESPACE::basename(path), "a");
+ ASSERT_STREQ(path, "///a");
+}
diff --git a/libc/test/src/libgen/dirname_death_test.cpp b/libc/test/src/libgen/dirname_death_test.cpp
new file mode 100644
index 0000000000000..e135a4ce60cd6
--- /dev/null
+++ b/libc/test/src/libgen/dirname_death_test.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Death tests for dirname.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/dirname.h"
+#include "test/UnitTest/Test.h"
+
+#ifdef ENABLE_SUBPROCESS_TESTS
+TEST(LlvmLibcDirnameTest, ModifyReturnValue) {
+ char *r = LIBC_NAMESPACE::dirname(nullptr);
+ ASSERT_DEATH([r]() { r[0] = 'a'; }, WITH_SIGNAL(-1));
+}
+#endif
diff --git a/libc/test/src/libgen/dirname_test.cpp b/libc/test/src/libgen/dirname_test.cpp
new file mode 100644
index 0000000000000..afd718fb6e559
--- /dev/null
+++ b/libc/test/src/libgen/dirname_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
+/// Unittests for dirname.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/libgen/dirname.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcDirnameTest, NullPointer) {
+ ASSERT_STREQ(LIBC_NAMESPACE::dirname(nullptr), ".");
+}
+
+TEST(LlvmLibcDirnameTest, EmptyString) {
+ char path[] = "";
+ ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), ".");
+}
+
+TEST(LlvmLibcDirnameTest, RegularPath) {
+ char path[] = "/usr/lib";
+ ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/usr");
+ ASSERT_STREQ(path, "/usr");
+}
+
+TEST(LlvmLibcDirnameTest, TrailingSlash) {
+ char path[] = "/usr/";
+ ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/");
+ ASSERT_STREQ(path, "/");
+}
+
+TEST(LlvmLibcDirnameTest, SingleSlash) {
+ char path[] = "/";
+ ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/");
+}
+
+TEST(LlvmLibcDirnameTest, MultipleSlashes) {
+ char path[] = "///";
+ ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/");
+}
+
+TEST(LlvmLibcDirnameTest, SimpleName) {
+ char path[] = "a";
+ ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), ".");
+}
+
+TEST(LlvmLibcDirnameTest, SimpleNameTrailingSlash) {
+ char path[] = "a/";
+ ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), ".");
+}
+
+TEST(LlvmLibcDirnameTest, ComplexPath) {
+ char path[] = "///a///b///";
+ ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "///a");
+ ASSERT_STREQ(path, "///a");
+}
+
+TEST(LlvmLibcDirnameTest, SlashA) {
+ char path[] = "/a";
+ ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/");
+ ASSERT_STREQ(path, "/");
+}
+
+TEST(LlvmLibcDirnameTest, MultipleSlashesA) {
+ char path[] = "///a";
+ ASSERT_STREQ(LIBC_NAMESPACE::dirname(path), "/");
+ ASSERT_STREQ(path, "/");
+}
diff --git a/libc/utils/docgen/libgen.yaml b/libc/utils/docgen/libgen.yaml
new file mode 100644
index 0000000000000..07aad5f1be55c
--- /dev/null
+++ b/libc/utils/docgen/libgen.yaml
@@ -0,0 +1,5 @@
+functions:
+ basename:
+ in-latest-posix: ''
+ dirname:
+ in-latest-posix: ''
>From a6db6b4e308d3f44fa1181297d267e09a1c4b996 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Tue, 23 Jun 2026 13:54:49 +0100
Subject: [PATCH 2/2] [libc] Support ASan death tests in LibcDeathTestExecutors
When running unit tests under AddressSanitizer, ASan intercepts
SIGSEGV and exits with a non-zero exit code instead of letting the
process be killed by the signal.
Modify the custom death test executor to recognize a non-zero exit
code as a successful death when compiled with ASan.
This allows the basename and dirname death tests to run and pass
under ASan on the buildbots, while keeping them as proper death
tests as requested by reviewers.
Assisted-by: Automated tooling, human reviewed.
---
libc/test/UnitTest/LibcDeathTestExecutors.cpp | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/libc/test/UnitTest/LibcDeathTestExecutors.cpp b/libc/test/UnitTest/LibcDeathTestExecutors.cpp
index 5f8d7b86a5187..af86a2bf17f8e 100644
--- a/libc/test/UnitTest/LibcDeathTestExecutors.cpp
+++ b/libc/test/UnitTest/LibcDeathTestExecutors.cpp
@@ -9,6 +9,7 @@
#include "LibcTest.h"
#include "src/__support/macros/config.h"
+#include "src/__support/macros/sanitizer.h"
#include "test/UnitTest/ExecuteFunction.h"
#include "test/UnitTest/TestLogger.h"
@@ -43,6 +44,14 @@ bool Test::testProcessKilled(testutils::FunctionCaller *Func, int Signal,
}
if (Result.exited_normally()) {
+#ifdef LIBC_HAS_ADDRESS_SANITIZER
+ // AddressSanitizer intercepts signals (like SIGSEGV) and prints a report.
+ // Instead of letting the process be killed by the signal, ASan calls
+ // exit() with a non-zero exit code. We accept this normal exit with a
+ // non-zero status as a successful death test under ASan.
+ if (Result.get_exit_code() != 0)
+ return true;
+#endif
Ctx->markFail();
tlog << Loc;
tlog << "Expected " << LHSStr
More information about the libc-commits
mailing list