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

via libcxx-commits libcxx-commits at lists.llvm.org
Sun Jul 12 23:05:20 PDT 2026


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

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

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   3 +
 libcxx/include/__config                       |  13 +-
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   4 +
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  14 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  41 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 293 insertions(+), 17 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..ff9d7d2b94e58 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#ifndef _LIBCPP_FREESTANDING
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..dd56cb6f91604 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
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -111,8 +115,13 @@
 #    define _LIBCPP_ALIGNOF(...) alignof(__VA_ARGS__)
 #    define _ALIGNAS_TYPE(x) alignas(x)
 #    define _ALIGNAS(x) alignas(x)
-#    define _NOEXCEPT noexcept
-#    define _NOEXCEPT_(...) noexcept(__VA_ARGS__)
+#    ifdef __cplusplus
+#      define _NOEXCEPT noexcept
+#      define _NOEXCEPT_(...) noexcept(__VA_ARGS__)
+#    else
+#      define _NOEXCEPT
+#      define _NOEXCEPT_(...)
+#    endif
 #    define _LIBCPP_CONSTEXPR constexpr
 
 #  else
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..052c2d00cc0ee 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
+#cmakedefine _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..3c4a1a7b0da03 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 defined(_LIBCPP_FREESTANDING)
+#  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..3fb2c7d4a67fb 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -57,6 +57,10 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+extern "C" {
+typedef __UINT_LEAST64_TYPE__ 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..190daf23285fa 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) _NOEXCEPT;
+long double atan2l(long double, long double) _NOEXCEPT;
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..b1985625d5b09 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -118,4 +118,18 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifndef EOF
+#      define EOF (-1)
+#    endif
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*) _NOEXCEPT;
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..43f1d24320fcb 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) _NOEXCEPT;
+lldiv_t lldiv(long long x, long long y) _NOEXCEPT;
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..fd70bcf621e1b 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,48 @@ 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
+#    ifdef __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* __restrict, const void* __restrict, size_t) _NOEXCEPT;
+void* memmove(void*, const void*, size_t) _NOEXCEPT;
+char* strcpy(char* __restrict, const char* __restrict) _NOEXCEPT;
+char* strncpy(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+char* strcat(char* __restrict, const char* __restrict) _NOEXCEPT;
+char* strncat(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+int memcmp(const void*, const void*, size_t) _NOEXCEPT;
+int strcmp(const char*, const char*) _NOEXCEPT;
+int strncmp(const char*, const char*, size_t) _NOEXCEPT;
+const void* memchr(const void*, int, size_t) _NOEXCEPT;
+const char* strchr(const char*, int) _NOEXCEPT;
+size_t strcspn(const char*, const char*) _NOEXCEPT;
+const char* strpbrk(const char*, const char*) _NOEXCEPT;
+const char* strrchr(const char*, int) _NOEXCEPT;
+size_t strspn(const char*, const char*) _NOEXCEPT;
+const char* strstr(const char*, const char*) _NOEXCEPT;
+char* strtok(char* __restrict, const char* __restrict) _NOEXCEPT;
+void* memset(void*, int, size_t) _NOEXCEPT;
+size_t strlen(const char*) _NOEXCEPT;
+size_t strnlen(const char*, size_t) _NOEXCEPT;
+void* memset_explicit(void*, int, size_t) _NOEXCEPT;
+int strcoll(const char*, const char*) _NOEXCEPT;
+size_t strxfrm(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+char* strerror(int) _NOEXCEPT;
+
+#    ifdef __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..e8e84bccccbac
--- /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 defined(_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