[libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR #208900)

via libcxx-commits libcxx-commits at lists.llvm.org
Sun Jul 12 07:45:07 PDT 2026


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 889e92d5cc788694f8dea10884073e8cd2067ac7 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libcxx] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   4 +-
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   7 +-
 libcxx/include/math.h                         |  39 +++++-
 libcxx/include/stdio.h                        |  15 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  43 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 293 insertions(+), 18 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..681eaac3ecb07 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -828,6 +828,7 @@ void __introsort(_RandomAccessIterator __first,
   }
 }
 
+#if _LIBCPP_FREESTANDING != 0
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
@@ -858,6 +859,7 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+#endif
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
@@ -957,4 +959,4 @@ _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP___ALGORITHM_SORT_H
+#endif // _LIBCPP___ALGORITHM_SORT_H
\ No newline at end of file
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..6140faed9550f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -52,11 +52,16 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #  include <__cstddef/size_t.h>
 
 // <time.h> is not provided by libc++
-#  if __has_include(<time.h>)
+#  if __STDC_HOSTED__ && __has_include(<time.h>)
 #    include <time.h>
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..0544429798186 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
@@ -531,4 +568,4 @@ using std::__math::trunc;
 #    include_next <math.h>
 #  endif
 
-#endif // _LIBCPP_MATH_H
+#endif // _LIBCPP_MATH_H
\ No newline at end of file
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..534ceb8188428 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,10 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
@@ -118,4 +122,15 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..98957183478e2 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,50 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+size_t strnlen(const char*, size_t);              /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifndef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..aff38cbede540
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#    include <iosfwd>
+#    include <cstdio>
+#    include <cerrno>
+#    include <cstring>
+#    include <cmath>
+#    ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#      include <cwchar>
+#    endif
+#    include <array>
+#    include <span>
+#    include <algorithm>
+#    include <bitset>
+#    include <variant>
+#    include <optional>
+#    include <numbers>
+#    include <scoped_allocator>
+#    include <typeindex>
+#    include <string_view>
+#    include <numeric>
+#    include <complex>
+#    include <chrono>
+#    include <charconv>
+#    include <expected>
+#    include <random>
+#    include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#    include <vector>
+#    include <deque>
+#    include <string>
+#    include <stack>
+#    include <queue>
+#    include <list>
+#    include <forward_list>
+#    include <map>
+#    include <set>
+#    include <unordered_set>
+#    include <unordered_map>
+#    if __has_include(<mdspan>)
+#      include <mdspan>
+#    endif
+#    include <valarray>
+
+#    if _LIBCPP_STD_VER < 20
+#      include <ciso646>
+#      if __has_include(<cstdalign>)
+#        include <cstdalign>
+#      endif
+#      include <cstdbool>
+#    endif
+#  endif



More information about the libcxx-commits mailing list