[libcxx-commits] [libcxx] [libcxxabi] varconst/hardening semantics introduce (PR #148268)

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Sat Jul 12 19:25:11 PDT 2025


https://github.com/var-const updated https://github.com/llvm/llvm-project/pull/148268

>From 6708ebf6020c59a4832aac95cb9524d533798100 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Fri, 11 Jul 2025 10:17:36 -0700
Subject: [PATCH 1/9] [libc++][hardening][NFC] Introduce `_LIBCPP_VERBOSE_TRAP`
 macro.

Split out the calls to `__builtin_verbose_trap` into a separate header.
This is just a refactoring to make the code a bit more structured.
---
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/__verbose_trap                 | 36 +++++++++++++++++++
 .../vendor/llvm/default_assertion_handler.in  | 14 ++------
 3 files changed, 39 insertions(+), 12 deletions(-)
 create mode 100644 libcxx/include/__verbose_trap

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index c8e6d28584623..f79edc9e32599 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -944,6 +944,7 @@ set(files
   __vector/vector_bool.h
   __vector/vector_bool_formatter.h
   __verbose_abort
+  __verbose_trap
   algorithm
   any
   array
diff --git a/libcxx/include/__verbose_trap b/libcxx/include/__verbose_trap
new file mode 100644
index 0000000000000..13ea727738c3b
--- /dev/null
+++ b/libcxx/include/__verbose_trap
@@ -0,0 +1,36 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___VERBOSE_TRAP
+#define _LIBCPP___VERBOSE_TRAP
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if __has_builtin(__builtin_verbose_trap)
+// AppleClang shipped a slightly different version of __builtin_verbose_trap from the upstream
+// version before upstream Clang actually got the builtin.
+// TODO: Remove once AppleClang supports the two-arguments version of the builtin.
+#  if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1700
+#    define _LIBCPP_VERBOSE_TRAP(message) __builtin_verbose_trap(message)
+#  else
+#    define _LIBCPP_VERBOSE_TRAP(message) __builtin_verbose_trap("libc++", message)
+#  endif
+#else
+#  define _LIBCPP_VERBOSE_TRAP(message) ((void)message, __builtin_trap())
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___VERBOSE_TRAP
diff --git a/libcxx/vendor/llvm/default_assertion_handler.in b/libcxx/vendor/llvm/default_assertion_handler.in
index 1d6b21fc6bb45..90b202a2dae57 100644
--- a/libcxx/vendor/llvm/default_assertion_handler.in
+++ b/libcxx/vendor/llvm/default_assertion_handler.in
@@ -15,6 +15,7 @@
 #  include <__cxx03/__verbose_abort>
 #else
 #  include <__config>
+#  include <__verbose_trap>
 #  include <__verbose_abort>
 #endif
 
@@ -28,18 +29,7 @@
 
 #else
 
-#  if __has_builtin(__builtin_verbose_trap)
-// AppleClang shipped a slightly different version of __builtin_verbose_trap from the upstream
-// version before upstream Clang actually got the builtin.
-// TODO: Remove once AppleClang supports the two-arguments version of the builtin.
-#    if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1700
-#      define _LIBCPP_ASSERTION_HANDLER(message) __builtin_verbose_trap(message)
-#    else
-#      define _LIBCPP_ASSERTION_HANDLER(message) __builtin_verbose_trap("libc++", message)
-#    endif
-#  else
-#    define _LIBCPP_ASSERTION_HANDLER(message) ((void)message, __builtin_trap())
-#  endif
+#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
 
 #endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
 

>From 1fcfec368e34964811270b9a10ad969814e3a691 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Fri, 11 Jul 2025 10:29:10 -0700
Subject: [PATCH 2/9] [libc++][hardening] Introduce a dylib function to log
 hardening errors.

Unlike `verbose_abort`, this function merely logs the error but does not
terminate execution. It is intended to make it possible to implement the
`observe` semantic for Hardening.
---
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/__configuration/availability.h |  5 ++
 libcxx/include/__log_hardening_failure        | 45 +++++++++++++++++
 libcxx/src/CMakeLists.txt                     |  1 +
 libcxx/src/log_hardening_failure.cpp          | 49 +++++++++++++++++++
 5 files changed, 101 insertions(+)
 create mode 100644 libcxx/include/__log_hardening_failure
 create mode 100644 libcxx/src/log_hardening_failure.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index c8e6d28584623..2f8be540e73e2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -535,6 +535,7 @@ set(files
   __locale_dir/time.h
   __locale_dir/wbuffer_convert.h
   __locale_dir/wstring_convert.h
+  __log_hardening_failure
   __math/abs.h
   __math/copysign.h
   __math/error_functions.h
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index ae58e36b508b4..cb72e927caa9c 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -304,6 +304,11 @@
 #define _LIBCPP_AVAILABILITY_HAS_VERBOSE_ABORT _LIBCPP_INTRODUCED_IN_LLVM_15
 #define _LIBCPP_AVAILABILITY_VERBOSE_ABORT _LIBCPP_INTRODUCED_IN_LLVM_15_ATTRIBUTE
 
+// This controls whether the library provides a function to log hardening failures without terminating the program (for
+// the `observe` assertion semantic).
+#define _LIBCPP_AVAILABILITY_HAS_LOG_HARDENING_FAILURE _LIBCPP_INTRODUCED_IN_LLVM_21
+#define _LIBCPP_AVAILABILITY_LOG_HARDENING_FAILURE _LIBCPP_INTRODUCED_IN_LLVM_21_ATTRIBUTE
+
 // This controls the availability of the C++17 std::pmr library,
 // which is implemented in large part in the built library.
 //
diff --git a/libcxx/include/__log_hardening_failure b/libcxx/include/__log_hardening_failure
new file mode 100644
index 0000000000000..73cff0ac64155
--- /dev/null
+++ b/libcxx/include/__log_hardening_failure
@@ -0,0 +1,45 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___LOG_HARDENING_FAILURE
+#define _LIBCPP___LOG_HARDENING_FAILURE
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// This function should never be called directly from the code -- it should only be called through the
+// `_LIBCPP_LOG_HARDENING_FAILURE` macro.
+_LIBCPP_AVAILABILITY_LOG_HARDENING_FAILURE _LIBCPP_OVERRIDABLE_FUNC_VIS void
+__libcpp_log_hardening_failure(const char* message) _NOEXCEPT;
+
+// _LIBCPP_LOG_HARDENING_FAILURE(message)
+//
+// This macro is used to log a hardening failure without terminating the program (as is the case if the `observe`
+// assertion semantic is used). Where possible, it logs in a way that indicates a fatal error (which might include
+// capturing the stack trace).
+#if !defined(_LIBCPP_LOG_HARDENING_FAILURE)
+
+#  if !_LIBCPP_AVAILABILITY_HAS_LOG_HARDENING_FAILURE
+// The decltype is there to suppress -Wunused warnings in this configuration.
+void __use(const char*);
+#    define _LIBCPP_LOG_HARDENING_FAILURE(message) (decltype(::std::__use(message))())
+#  else
+#    define _LIBCPP_LOG_HARDENING_FAILURE(message) ::std::__libcpp_log_hardening_failure(message)
+#  endif
+
+#endif // !defined(_LIBCPP_LOG_HARDENING_FAILURE)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___LOG_HARDENING_FAILURE
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 97fe57a5f24f8..926deb3a1c732 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -30,6 +30,7 @@ set(LIBCXX_SOURCES
   include/ryu/ryu.h
   include/to_chars_floating_point.h
   include/from_chars_floating_point.h
+  log_hardening_failure.cpp
   memory.cpp
   memory_resource.cpp
   new_handler.cpp
diff --git a/libcxx/src/log_hardening_failure.cpp b/libcxx/src/log_hardening_failure.cpp
new file mode 100644
index 0000000000000..7e408a6f010b4
--- /dev/null
+++ b/libcxx/src/log_hardening_failure.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__log_hardening_failure>
+#include <cstdio>
+
+#ifdef __BIONIC__
+#  include <syslog.h>
+extern "C" void android_set_abort_message(const char* msg);
+#endif // __BIONIC__
+
+#if defined(__APPLE__) && __has_include(<os/reason_private.h>)
+#  include <os/reason_private.h>
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+_LIBCPP_WEAK void __libcpp_log_hardening_failure(const char* message) noexcept {
+  // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash.
+#if defined(__APPLE__) && __has_include(<os/reason_private.h>)
+  os_fault_with_payload(
+      /*reason_namespace=*/OS_REASON_SECURITY,
+      /*reason_code=*/0,
+      /*payload=*/nullptr,
+      /*payload_size=*/0,
+      /*reason_string=*/message,
+      /*reason_flags=*/0);
+
+#elif defined(__BIONIC__)
+  // Show error in tombstone.
+  android_set_abort_message(message);
+
+  // Show error in logcat.
+  openlog("libc++", 0, 0);
+  syslog(LOG_CRIT, "%s", message);
+  closelog();
+
+#else
+  fprintf(stderr, "%s", message);
+#endif
+}
+
+_LIBCPP_END_NAMESPACE_STD

>From debdf1452cb4a3ba5258313f9b5c0fae146dd576 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Fri, 11 Jul 2025 10:58:57 -0700
Subject: [PATCH 3/9] [libc++][hardening] Introduce assertion semantics.

---
 libcxx/include/__config                       |  30 +++++
 libcxx/test/support/check_assertion.h         | 120 ++++++++++++++++--
 .../vendor/llvm/default_assertion_handler.in  |  21 ++-
 libcxxabi/src/CMakeLists.txt                  |  15 +--
 libcxxabi/src/demangle/DemangleConfig.h       |   5 +
 libcxxabi/src/log_error_and_continue.cpp      |  48 +++++++
 libcxxabi/src/log_error_and_continue.h        |  16 +++
 7 files changed, 229 insertions(+), 26 deletions(-)
 create mode 100644 libcxxabi/src/log_error_and_continue.cpp
 create mode 100644 libcxxabi/src/log_error_and_continue.h

diff --git a/libcxx/include/__config b/libcxx/include/__config
index d940461c30234..2223d466c2a7f 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -147,6 +147,36 @@ _LIBCPP_HARDENING_MODE_EXTENSIVE, \
 _LIBCPP_HARDENING_MODE_DEBUG
 #  endif
 
+// Hardening assertion semantics mirror the evaluation semantics of P3100 Contracts:
+// - `ignore` does not evaluate the assertion;
+// - `observe` logs an error (indicating, if possible, that the error is fatal) and continues execution;
+// - `quick-enforce` terminates the program as fast as possible (via trapping);
+// - `enforce` logs an error and then terminates the program.
+// Notes:
+// - Continuing execution after a hardening check fails results in undefined behavior; the `observe` semantic is meant
+//   to make adopting hardening easier but should not be used outside of this scenario;
+// - P3471 "Standard Library Hardening" wording precludes using the Contracts `ignore` semantic for hardened
+//   preconditions in the Library; allowing this semantic to be used is a libc++ vendor extension.
+// clang-format off
+#  define _LIBCPP_ASSERTION_SEMANTIC_IGNORE        (1 << 1)
+#  define _LIBCPP_ASSERTION_SEMANTIC_OBSERVE       (1 << 2)
+#  define _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE (1 << 3)
+#  define _LIBCPP_ASSERTION_SEMANTIC_ENFORCE       (1 << 4)
+// clang-format on
+
+// Allow users to define an arbitrary assertion semantic; otherwise, use the default mapping from modes to semantics.
+// The default is for production-capable modes to use `quick-enforce` (i.e., trap) and for the `debug` mode to use
+// `enforce` (i.e., log and abort).
+#  ifndef _LIBCPP_ASSERTION_SEMANTIC
+
+#    if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#      define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
+#    else
+#      define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
+#    endif
+
+#  endif // _LIBCPP_ASSERTION_SEMANTIC
+
 // } HARDENING
 
 #  define _LIBCPP_TOSTRING2(x) #x
diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h
index a279400d651b4..7f8102d82970d 100644
--- a/libcxx/test/support/check_assertion.h
+++ b/libcxx/test/support/check_assertion.h
@@ -50,9 +50,17 @@ MatchResult MatchAssertionMessage(const std::string& text, std::string_view expe
   std::regex assertion_format(".*###\\n(.*):(\\d+): assertion (.*) failed: (.*)\\n###");
 
   std::smatch match_result;
-  bool has_match = std::regex_match(text, match_result, assertion_format);
-  assert(has_match);
-  assert(match_result.size() == 5);
+  // If a non-terminating assertion semantic is used, more than one assertion might be triggered before the process
+  // dies, so we cannot expect the entire target string to match.
+  bool has_match = std::regex_search(text, match_result, assertion_format);
+  if (!has_match || match_result.size() != 5) {
+    std::stringstream matching_error;
+    matching_error                                                     //
+        << "Failed to parse the assertion message.\n"                  //
+        << "Expected message:   '" << expected_message.data() << "'\n" //
+        << "Stderr contents:    '" << text.c_str() << "'\n";
+    return MatchResult(/*success=*/false, matching_error.str());
+  }
 
   const std::string& file = match_result[1];
   int line                = std::stoi(match_result[2]);
@@ -85,13 +93,16 @@ Matcher MakeAnyMatcher() {
 }
 
 enum class DeathCause {
-  // Valid causes
+  // Valid causes.
   VerboseAbort = 1,
   StdAbort,
   StdTerminate,
   Trap,
-  // Invalid causes
+  // Causes that might be invalid or might stem from undefined behavior (relevant for non-terminating assertion
+  // semantics).
   DidNotDie,
+  Segfault,
+  // Always invalid causes.
   SetupFailure,
   Unknown
 };
@@ -108,6 +119,16 @@ bool IsValidCause(DeathCause cause) {
   }
 }
 
+bool IsTestSetupErrorCause(DeathCause cause) {
+  switch (cause) {
+  case DeathCause::SetupFailure:
+  case DeathCause::Unknown:
+    return true;
+  default:
+    return false;
+  }
+}
+
 std::string ToString(DeathCause cause) {
   switch (cause) {
   case DeathCause::VerboseAbort:
@@ -120,10 +141,12 @@ std::string ToString(DeathCause cause) {
     return "trap";
   case DeathCause::DidNotDie:
     return "<invalid cause (child did not die)>";
+  case DeathCause::Segfault:
+    return "<invalid cause (segmentation fault)>";
   case DeathCause::SetupFailure:
-    return "<invalid cause (child failed to set up test environment)>";
+    return "<test setup error (child failed to set up test environment)>";
   case DeathCause::Unknown:
-    return "<invalid cause (cause unknown)>";
+    return "<test setup error (test doesn't know how to interpret the death cause)>";
   }
 
   assert(false && "Unreachable");
@@ -225,9 +248,38 @@ class DeathTest {
     return DeathTestResult(Outcome::Success, cause);
   }
 
-  void PrintFailureDetails(std::string_view failure_description, std::string_view stmt, DeathCause cause) const {
-    std::fprintf(
-        stderr, "Failure: EXPECT_DEATH( %s ) failed!\n(reason: %s)\n\n", stmt.data(), failure_description.data());
+  // When non-terminating assertion semantics are used, the program will invoke UB which might or might not crash the
+  // process; we make sure that the execution produces the expected error message but otherwise consider the test run
+  // successful whether the child process dies or not.
+  template <class Func>
+  DeathTestResult RunWithoutGuaranteedDeath(Func&& func, const Matcher& matcher) {
+    std::signal(SIGABRT, [](int) { StopChildProcess(DeathCause::StdAbort); });
+    std::set_terminate([] { StopChildProcess(DeathCause::StdTerminate); });
+
+    DeathCause cause = Run(func);
+
+    if (IsTestSetupErrorCause(cause)) {
+      return DeathTestResult(Outcome::InvalidCause, cause, ToString(cause));
+    }
+
+    MatchResult match_result = matcher(GetChildStdErr());
+    if (!match_result.first) {
+      auto failure_description = std::string("Child produced a different error message\n") + match_result.second;
+      return DeathTestResult(Outcome::UnexpectedErrorMessage, cause, failure_description);
+    }
+
+    return DeathTestResult(Outcome::Success, cause);
+  }
+
+  void PrintFailureDetails(std::string_view invocation,
+                           std::string_view failure_description,
+                           std::string_view stmt,
+                           DeathCause cause) const {
+    std::fprintf(stderr,
+                 "Failure: %s( %s ) failed!\n(reason: %s)\n\n",
+                 invocation.data(),
+                 stmt.data(),
+                 failure_description.data());
 
     if (cause != DeathCause::Unknown) {
       std::fprintf(stderr, "child exit code: %d\n", GetChildExitCode());
@@ -311,10 +363,13 @@ class DeathTest {
 
     if (WIFSIGNALED(status_value)) {
       exit_code_ = WTERMSIG(status_value);
-      // `__builtin_trap` generqtes `SIGILL` on x86 and `SIGTRAP` on ARM.
+      // `__builtin_trap` generates `SIGILL` on x86 and `SIGTRAP` on ARM.
       if (exit_code_ == SIGILL || exit_code_ == SIGTRAP) {
         return DeathCause::Trap;
       }
+      if (exit_code_ == SIGSEGV) {
+        return DeathCause::Segfault;
+      }
     }
 
     return DeathCause::Unknown;
@@ -334,6 +389,12 @@ class DeathTest {
 };
 
 #ifdef _LIBCPP_VERSION
+void std::__libcpp_log_hardening_failure(const char* message) noexcept {
+  std::fprintf(stderr, "%s\n", Marker);
+  std::fprintf(stderr, "%s", message);
+  std::fprintf(stderr, "%s\n", Marker);
+}
+
 void std::__libcpp_verbose_abort(char const* format, ...) noexcept {
   va_list args;
   va_start(args, format);
@@ -357,7 +418,7 @@ bool ExpectDeath(
   DeathTest test_case;
   DeathTestResult test_result = test_case.Run(expected_causes, func, matcher);
   if (!test_result.success()) {
-    test_case.PrintFailureDetails(test_result.failure_description(), stmt, test_result.cause());
+    test_case.PrintFailureDetails("EXPECT_DEATH", test_result.failure_description(), stmt, test_result.cause());
   }
 
   return test_result.success();
@@ -378,6 +439,22 @@ bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
   return ExpectDeath(std::array<DeathCause, 1>{expected_cause}, stmt, func, MakeAnyMatcher());
 }
 
+template <class Func>
+bool ExpectLog(const char* stmt, Func&& func, const Matcher& matcher) {
+  DeathTest test_case;
+  DeathTestResult test_result = test_case.RunWithoutGuaranteedDeath(func, matcher);
+  if (!test_result.success()) {
+    test_case.PrintFailureDetails("EXPECT_LOG", test_result.failure_description(), stmt, test_result.cause());
+  }
+
+  return test_result.success();
+}
+
+template <class Func>
+bool ExpectLog(const char* stmt, Func&& func) {
+  return ExpectLog(stmt, func, MakeAnyMatcher());
+}
+
 // clang-format off
 
 /// Assert that the specified expression aborts with the expected cause and, optionally, error message.
@@ -392,13 +469,28 @@ bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
 #define EXPECT_STD_TERMINATE(...)                 \
     assert(  ExpectDeath(DeathCause::StdTerminate, #__VA_ARGS__, __VA_ARGS__)  )
 
-#if defined(_LIBCPP_HARDENING_MODE) && _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#if defined(_LIBCPP_ASSERTION_SEMANTIC)
+
+#if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
 #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
     assert(( ExpectDeath(DeathCause::VerboseAbort, #expr, [&]() { (void)(expr); }, MakeAssertionMessageMatcher(message)) ))
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
+#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
+    assert(( ExpectDeath(DeathCause::Trap,         #expr, [&]() { (void)(expr); }) ))
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
+#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
+    assert(( ExpectLog(#expr, [&]() { (void)(expr); }, MakeAssertionMessageMatcher(message)) ))
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
+#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
+    assert(( ExpectLog(#expr, [&]() { (void)(expr); }) ))
+#else
+#error "_LIBCPP_ASSERTION_SEMANTIC is set to an invalid value"
+#endif // _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
+
 #else
 #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
     assert(( ExpectDeath(DeathCause::Trap,         #expr, [&]() { (void)(expr); }) ))
-#endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#endif // defined(_LIBCPP_ASSERTION_SEMANTIC)
 
 // clang-format on
 
diff --git a/libcxx/vendor/llvm/default_assertion_handler.in b/libcxx/vendor/llvm/default_assertion_handler.in
index 90b202a2dae57..4ece031c03ece 100644
--- a/libcxx/vendor/llvm/default_assertion_handler.in
+++ b/libcxx/vendor/llvm/default_assertion_handler.in
@@ -15,22 +15,35 @@
 #  include <__cxx03/__verbose_abort>
 #else
 #  include <__config>
-#  include <__verbose_trap>
+#  include <__log_hardening_failure>
 #  include <__verbose_abort>
+#  include <__verbose_trap>
 #endif
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
-#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
+#  define _LIBCPP_ASSERTION_HANDLER(message) ((void)0)
+
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
+#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_LOG_HARDENING_FAILURE(message)
 
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
+#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
+
+#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
 #  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
 
 #else
 
-#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
+#  error _LIBCPP_ASSERTION_SEMANTIC must be set to one of the following values: \
+_LIBCPP_ASSERTION_SEMANTIC_IGNORE, \
+_LIBCPP_ASSERTION_SEMANTIC_OBSERVE, \
+_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE, \
+_LIBCPP_ASSERTION_SEMANTIC_ENFORCE
 
-#endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+#endif // _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
 
 #endif // _LIBCPP___ASSERTION_HANDLER
diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index 0675577aed6a1..9a9a65ce3aaa1 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -16,6 +16,7 @@ set(LIBCXXABI_SOURCES
   # Internal files
   abort_message.cpp
   fallback_malloc.cpp
+  log_error_and_continue.cpp
   private_typeinfo.cpp
 )
 
@@ -172,12 +173,11 @@ if (LIBCXXABI_USE_LLVM_UNWINDER)
     target_link_libraries(cxxabi_shared_objects PUBLIC unwind_shared)
   endif()
 endif()
-target_link_libraries(cxxabi_shared_objects
-  PUBLIC cxxabi-headers
-  PRIVATE cxx-headers runtimes-libc-headers ${LIBCXXABI_LIBRARIES})
+target_link_libraries(cxxabi_shared_objects PRIVATE cxx-headers ${LIBCXXABI_LIBRARIES})
 if (NOT CXX_SUPPORTS_NOSTDLIBXX_FLAG)
   target_link_libraries(cxxabi_shared_objects PRIVATE ${LIBCXXABI_BUILTINS_LIBRARY})
 endif()
+target_link_libraries(cxxabi_shared_objects PUBLIC cxxabi-headers)
 set_target_properties(cxxabi_shared_objects
   PROPERTIES
     CXX_EXTENSIONS OFF
@@ -216,7 +216,7 @@ if (ZOS)
 endif ()
 
 target_link_libraries(cxxabi_shared
-  PUBLIC cxxabi_shared_objects runtimes-libc-shared
+  PUBLIC cxxabi_shared_objects
   PRIVATE ${LIBCXXABI_LIBRARIES})
 
 if (LIBCXXABI_ENABLE_SHARED)
@@ -275,9 +275,8 @@ if (LIBCXXABI_USE_LLVM_UNWINDER AND LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC
   target_link_libraries(cxxabi_static_objects PUBLIC unwind_static_objects) # propagate usage requirements
   target_sources(cxxabi_static_objects PUBLIC $<TARGET_OBJECTS:unwind_static_objects>)
 endif()
-target_link_libraries(cxxabi_static_objects
-  PUBLIC cxxabi-headers
-  PRIVATE cxx-headers runtimes-libc-headers ${LIBCXXABI_STATIC_LIBRARIES} ${LIBCXXABI_LIBRARIES})
+target_link_libraries(cxxabi_static_objects PRIVATE cxx-headers ${LIBCXXABI_STATIC_LIBRARIES} ${LIBCXXABI_LIBRARIES})
+target_link_libraries(cxxabi_static_objects PUBLIC cxxabi-headers)
 set_target_properties(cxxabi_static_objects
   PROPERTIES
     CXX_EXTENSIONS OFF
@@ -313,7 +312,7 @@ endif()
 
 add_library(cxxabi_static STATIC)
 if (LIBCXXABI_USE_LLVM_UNWINDER AND NOT LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_STATIC_LIBRARY)
-  target_link_libraries(cxxabi_static PUBLIC unwind_static runtimes-libc-static)
+  target_link_libraries(cxxabi_static PUBLIC unwind_static)
 endif()
 set_target_properties(cxxabi_static
   PROPERTIES
diff --git a/libcxxabi/src/demangle/DemangleConfig.h b/libcxxabi/src/demangle/DemangleConfig.h
index 06fd223f5553f..994d59507588f 100644
--- a/libcxxabi/src/demangle/DemangleConfig.h
+++ b/libcxxabi/src/demangle/DemangleConfig.h
@@ -19,6 +19,11 @@
 #include "../abort_message.h"
 #endif
 
+#ifndef _LIBCPP_LOG_HARDENING_FAILURE
+#define _LIBCPP_LOG_HARDENING_FAILURE(message) __log_error_and_continue(message)
+#include "../log_error_and_continue.h"
+#endif
+
 #include <version>
 
 #ifdef _MSC_VER
diff --git a/libcxxabi/src/log_error_and_continue.cpp b/libcxxabi/src/log_error_and_continue.cpp
new file mode 100644
index 0000000000000..51088cd11d914
--- /dev/null
+++ b/libcxxabi/src/log_error_and_continue.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
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "log_error_and_continue.h"
+
+#ifdef __BIONIC__
+#  include <syslog.h>
+extern "C" void android_set_abort_message(const char* msg);
+#endif // __BIONIC__
+
+#if defined(__APPLE__) && __has_include(<os/reason_private.h>)
+#  include <os/reason_private.h>
+#   define _LIBCXXABI_USE_OS_FAULT
+#endif
+
+void __log_error_and_continue(const char* message)
+{
+  // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash.
+#if defined(_LIBCXXABI_USE_OS_FAULT)
+  os_fault_with_payload(
+      /*reason_namespace=*/OS_REASON_SECURITY,
+      /*reason_code=*/0,
+      /*payload=*/nullptr,
+      /*payload_size=*/0,
+      /*reason_string=*/message,
+      /*reason_flags=*/0);
+
+#elif defined(__BIONIC__)
+  // Show error in tombstone.
+  android_set_abort_message(message);
+
+  // Show error in logcat.
+  openlog("libc++", 0, 0);
+  syslog(LOG_CRIT, "%s", message);
+  closelog();
+
+#else
+  fprintf(stderr, "%s", message);
+#endif
+}
diff --git a/libcxxabi/src/log_error_and_continue.h b/libcxxabi/src/log_error_and_continue.h
new file mode 100644
index 0000000000000..e976c1de65d06
--- /dev/null
+++ b/libcxxabi/src/log_error_and_continue.h
@@ -0,0 +1,16 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __LOG_ERROR_AND_CONTINUE_H_
+#define __LOG_ERROR_AND_CONTINUE_H_
+
+#include "cxxabi.h"
+
+extern "C" _LIBCXXABI_HIDDEN void __log_error_and_continue(const char* message);
+
+#endif // __LOG_ERROR_AND_CONTINUE_H_

>From 3ca83e671aa6b147cd855fc22d127d9b2fd16915 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Sat, 12 Jul 2025 01:11:47 -0700
Subject: [PATCH 4/9] Address feedback

---
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/__cxx03/__verbose_trap         | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../vendor/llvm/default_assertion_handler.in  |  3 +-
 4 files changed, 42 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__cxx03/__verbose_trap

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index f79edc9e32599..4f2a8dddad92c 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1570,6 +1570,7 @@ set(files
   __cxx03/__utility/unreachable.h
   __cxx03/__variant/monostate.h
   __cxx03/__verbose_abort
+  __cxx03/__verbose_trap
   __cxx03/algorithm
   __cxx03/array
   __cxx03/atomic
diff --git a/libcxx/include/__cxx03/__verbose_trap b/libcxx/include/__cxx03/__verbose_trap
new file mode 100644
index 0000000000000..755124b97a5ac
--- /dev/null
+++ b/libcxx/include/__cxx03/__verbose_trap
@@ -0,0 +1,36 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___CXX03___VERBOSE_TRAP
+#define _LIBCPP___CXX03___VERBOSE_TRAP
+
+#include <__cxx03/__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if __has_builtin(__builtin_verbose_trap)
+// AppleClang shipped a slightly different version of __builtin_verbose_trap from the upstream
+// version before upstream Clang actually got the builtin.
+// TODO: Remove once AppleClang supports the two-arguments version of the builtin.
+#  if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1700
+#    define _LIBCPP_VERBOSE_TRAP(message) __builtin_verbose_trap(message)
+#  else
+#    define _LIBCPP_VERBOSE_TRAP(message) __builtin_verbose_trap("libc++", message)
+#  endif
+#else
+#  define _LIBCPP_VERBOSE_TRAP(message) ((void)message, __builtin_trap())
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___CXX03___VERBOSE_TRAP
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 45b9c72a05b82..61ba1c381b2b3 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2356,6 +2356,9 @@ module std [system] {
   module verbose_abort {
     header "__verbose_abort"
   }
+  module verbose_trap {
+    header "__verbose_trap"
+  }
   module internal_assert {
     header "__assert"
     export *
diff --git a/libcxx/vendor/llvm/default_assertion_handler.in b/libcxx/vendor/llvm/default_assertion_handler.in
index 90b202a2dae57..f115658f9f3c6 100644
--- a/libcxx/vendor/llvm/default_assertion_handler.in
+++ b/libcxx/vendor/llvm/default_assertion_handler.in
@@ -13,10 +13,11 @@
 #if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 #  include <__cxx03/__config>
 #  include <__cxx03/__verbose_abort>
+#  include <__cxx03/__verbose_trap>
 #else
 #  include <__config>
-#  include <__verbose_trap>
 #  include <__verbose_abort>
+#  include <__verbose_trap>
 #endif
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)

>From ef34aded6cfa935fe9d904bb8d4f79bfb705137a Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Sat, 12 Jul 2025 01:58:15 -0700
Subject: [PATCH 5/9] Address feedback

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 libcxx/include/__configuration/availability.h |  8 +--
 libcxx/include/__log_error                    | 50 +++++++++++++++++++
 libcxx/include/__log_hardening_failure        | 45 -----------------
 libcxx/include/module.modulemap.in            |  3 ++
 libcxx/src/CMakeLists.txt                     |  2 +-
 ...og_hardening_failure.cpp => log_error.cpp} | 16 +++++-
 7 files changed, 73 insertions(+), 53 deletions(-)
 create mode 100644 libcxx/include/__log_error
 delete mode 100644 libcxx/include/__log_hardening_failure
 rename libcxx/src/{log_hardening_failure.cpp => log_error.cpp} (81%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 2f8be540e73e2..ceaf45e9e8ae6 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -535,7 +535,7 @@ set(files
   __locale_dir/time.h
   __locale_dir/wbuffer_convert.h
   __locale_dir/wstring_convert.h
-  __log_hardening_failure
+  __log_error
   __math/abs.h
   __math/copysign.h
   __math/error_functions.h
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index cb72e927caa9c..5de0b98ba22a9 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -304,10 +304,10 @@
 #define _LIBCPP_AVAILABILITY_HAS_VERBOSE_ABORT _LIBCPP_INTRODUCED_IN_LLVM_15
 #define _LIBCPP_AVAILABILITY_VERBOSE_ABORT _LIBCPP_INTRODUCED_IN_LLVM_15_ATTRIBUTE
 
-// This controls whether the library provides a function to log hardening failures without terminating the program (for
-// the `observe` assertion semantic).
-#define _LIBCPP_AVAILABILITY_HAS_LOG_HARDENING_FAILURE _LIBCPP_INTRODUCED_IN_LLVM_21
-#define _LIBCPP_AVAILABILITY_LOG_HARDENING_FAILURE _LIBCPP_INTRODUCED_IN_LLVM_21_ATTRIBUTE
+// This controls whether the library provides a function to log errors without terminating the program (used in
+// particular by the `observe` assertion semantic).
+#define _LIBCPP_AVAILABILITY_HAS_LOG_ERROR _LIBCPP_INTRODUCED_IN_LLVM_21
+#define _LIBCPP_AVAILABILITY_LOG_ERROR _LIBCPP_INTRODUCED_IN_LLVM_21_ATTRIBUTE
 
 // This controls the availability of the C++17 std::pmr library,
 // which is implemented in large part in the built library.
diff --git a/libcxx/include/__log_error b/libcxx/include/__log_error
new file mode 100644
index 0000000000000..9f308898247c4
--- /dev/null
+++ b/libcxx/include/__log_error
@@ -0,0 +1,50 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___LOG_ERROR
+#define _LIBCPP___LOG_ERROR
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+enum class _LogErrorReason {
+  // Where possible, it logs in a way that indicates a fatal error (which might include capturing the stack trace).
+  _HardeningFailure
+};
+
+// This function should never be called directly from the code -- it should only be called through the
+// `_LIBCPP_LOG_ERROR` macro.
+_LIBCPP_AVAILABILITY_LOG_ERROR _LIBCPP_EXPORTED_FROM_ABI void
+__log_error(_LogErrorReason __reason, const char* __message) _NOEXCEPT;
+
+// _LIBCPP_LOG_ERROR(message)
+//
+// This macro is used to log an error without terminating the program (as is the case for hardening failures if the
+// `observe` assertion semantic is used, for example).
+
+#if !defined(_LIBCPP_LOG_ERROR)
+
+#  if !_LIBCPP_AVAILABILITY_HAS_LOG_ERROR
+// The decltype is there to suppress -Wunused warnings in this configuration.
+void __use(const char*);
+#    define _LIBCPP_LOG_ERROR(__message) (decltype(::std::__use(__message))())
+#  else
+#    define _LIBCPP_LOG_ERROR(__reason, __message) ::std::__log_error(__reason, __message)
+#  endif
+
+#endif // !defined(_LIBCPP_LOG_ERROR)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___LOG_ERROR
diff --git a/libcxx/include/__log_hardening_failure b/libcxx/include/__log_hardening_failure
deleted file mode 100644
index 73cff0ac64155..0000000000000
--- a/libcxx/include/__log_hardening_failure
+++ /dev/null
@@ -1,45 +0,0 @@
-// -*- C++ -*-
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP___LOG_HARDENING_FAILURE
-#define _LIBCPP___LOG_HARDENING_FAILURE
-
-#include <__config>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-// This function should never be called directly from the code -- it should only be called through the
-// `_LIBCPP_LOG_HARDENING_FAILURE` macro.
-_LIBCPP_AVAILABILITY_LOG_HARDENING_FAILURE _LIBCPP_OVERRIDABLE_FUNC_VIS void
-__libcpp_log_hardening_failure(const char* message) _NOEXCEPT;
-
-// _LIBCPP_LOG_HARDENING_FAILURE(message)
-//
-// This macro is used to log a hardening failure without terminating the program (as is the case if the `observe`
-// assertion semantic is used). Where possible, it logs in a way that indicates a fatal error (which might include
-// capturing the stack trace).
-#if !defined(_LIBCPP_LOG_HARDENING_FAILURE)
-
-#  if !_LIBCPP_AVAILABILITY_HAS_LOG_HARDENING_FAILURE
-// The decltype is there to suppress -Wunused warnings in this configuration.
-void __use(const char*);
-#    define _LIBCPP_LOG_HARDENING_FAILURE(message) (decltype(::std::__use(message))())
-#  else
-#    define _LIBCPP_LOG_HARDENING_FAILURE(message) ::std::__libcpp_log_hardening_failure(message)
-#  endif
-
-#endif // !defined(_LIBCPP_LOG_HARDENING_FAILURE)
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP___LOG_HARDENING_FAILURE
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 45b9c72a05b82..763d32296a03d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2353,6 +2353,9 @@ module std [system] {
     header "__std_mbstate_t.h"
     export *
   }
+  module log_error {
+    header "__log_error"
+  }
   module verbose_abort {
     header "__verbose_abort"
   }
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 926deb3a1c732..0e83b2728aa5a 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -30,7 +30,7 @@ set(LIBCXX_SOURCES
   include/ryu/ryu.h
   include/to_chars_floating_point.h
   include/from_chars_floating_point.h
-  log_hardening_failure.cpp
+  log_error.cpp
   memory.cpp
   memory_resource.cpp
   new_handler.cpp
diff --git a/libcxx/src/log_hardening_failure.cpp b/libcxx/src/log_error.cpp
similarity index 81%
rename from libcxx/src/log_hardening_failure.cpp
rename to libcxx/src/log_error.cpp
index 7e408a6f010b4..01ddaf65bf618 100644
--- a/libcxx/src/log_hardening_failure.cpp
+++ b/libcxx/src/log_error.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
-#include <__log_hardening_failure>
+#include <__log_error>
 #include <cstdio>
 
 #ifdef __BIONIC__
@@ -21,7 +21,9 @@ extern "C" void android_set_abort_message(const char* msg);
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-_LIBCPP_WEAK void __libcpp_log_hardening_failure(const char* message) noexcept {
+namespace {
+
+void log_fatal_error(const char* message) noexcept {
   // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash.
 #if defined(__APPLE__) && __has_include(<os/reason_private.h>)
   os_fault_with_payload(
@@ -46,4 +48,14 @@ _LIBCPP_WEAK void __libcpp_log_hardening_failure(const char* message) noexcept {
 #endif
 }
 
+} // namespace
+
+void __log_error(_LogErrorReason reason, const char* message) noexcept {
+  switch (reason) {
+  case _LogErrorReason::_HardeningFailure:
+  default:
+    log_fatal_error(message);
+  }
+}
+
 _LIBCPP_END_NAMESPACE_STD

>From 01eb85de6072d3b188cf357573826f663ebfa51d Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Sat, 12 Jul 2025 15:01:05 -0700
Subject: [PATCH 6/9] Update the ABI list plus minor tweaks.

---
 ...le-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 5 +++--
 libcxx/src/CMakeLists.txt                                 | 2 +-
 libcxx/src/log_error.cpp                                  | 4 ++--
 libcxx/utils/libcxx/test/features.py                      | 8 ++++++++
 4 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 162757c7e37ec..bc6bdca5b8a2d 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -534,6 +534,7 @@
 {'is_defined': True, 'name': '__ZNKSt3__115basic_streambufIwNS_11char_traitsIwEEE6getlocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__115error_condition7messageEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__117bad_function_call4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__117moneypunct_bynameIcLb0EE11do_groupingEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__117moneypunct_bynameIcLb0EE13do_neg_formatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__117moneypunct_bynameIcLb0EE13do_pos_formatEv', 'type': 'FUNC'}
@@ -944,6 +945,7 @@
 {'is_defined': True, 'name': '__ZNSt3__110to_wstringEx', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__110to_wstringEy', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__111__call_onceERVmPvPFvS2_E', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__111__log_errorENS_15_LogErrorReasonEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__111__money_getIcE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_SF_Ri', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__111__money_getIwE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_SJ_Ri', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__111__money_putIcE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_Ri', 'type': 'FUNC'}
@@ -1125,6 +1127,7 @@
 {'is_defined': True, 'name': '__ZNSt3__112system_errorD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112system_errorD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112system_errorD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__113__hash_memoryEPKvm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE11__read_modeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE12__write_modeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4openEPKcj', 'type': 'FUNC'}
@@ -1305,7 +1308,6 @@
 {'is_defined': True, 'name': '__ZNSt3__113shared_futureIvED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113shared_futureIvED2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113shared_futureIvEaSERKS1_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__113__hash_memoryEPKvm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__114__num_get_base10__get_baseERNS_8ios_baseE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__114__num_get_base5__srcE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__114__num_put_base12__format_intEPcPKcbj', 'type': 'FUNC'}
@@ -1508,7 +1510,6 @@
 {'is_defined': True, 'name': '__ZNSt3__117bad_function_callD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117bad_function_callD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117bad_function_callD2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__117bad_function_call4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117iostream_categoryEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb0EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 0e83b2728aa5a..fe8e57690a880 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -30,8 +30,8 @@ set(LIBCXX_SOURCES
   include/ryu/ryu.h
   include/to_chars_floating_point.h
   include/from_chars_floating_point.h
-  log_error.cpp
   memory.cpp
+  log_error.cpp
   memory_resource.cpp
   new_handler.cpp
   new_helpers.cpp
diff --git a/libcxx/src/log_error.cpp b/libcxx/src/log_error.cpp
index 01ddaf65bf618..53cf3c7aa7ac2 100644
--- a/libcxx/src/log_error.cpp
+++ b/libcxx/src/log_error.cpp
@@ -23,7 +23,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace {
 
-void log_fatal_error(const char* message) noexcept {
+void log_security_failure(const char* message) noexcept {
   // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash.
 #if defined(__APPLE__) && __has_include(<os/reason_private.h>)
   os_fault_with_payload(
@@ -54,7 +54,7 @@ void __log_error(_LogErrorReason reason, const char* message) noexcept {
   switch (reason) {
   case _LogErrorReason::_HardeningFailure:
   default:
-    log_fatal_error(message);
+    log_security_failure(message);
   }
 }
 
diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
index 0cb81546665d4..8433ea178080b 100644
--- a/libcxx/utils/libcxx/test/features.py
+++ b/libcxx/utils/libcxx/test/features.py
@@ -894,6 +894,14 @@ def check_gdb(cfg):
             cfg.available_features,
         ),
     ),
+    # Tests that require __log_error support in the built library
+    Feature(
+        name="availability-log_error-missing",
+        when=lambda cfg: BooleanExpression.evaluate(
+            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-21)",
+            cfg.available_features,
+        ),
+    ),
     # Tests that require std::pmr support in the built library
     Feature(
         name="availability-pmr-missing",

>From 189cdc1809855f6d02ad68982a017facdd787634 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Sat, 12 Jul 2025 15:58:27 -0700
Subject: [PATCH 7/9] Address feedback.

---
 libcxx/include/__config                       | 12 +++--
 .../vendor/llvm/default_assertion_handler.in  |  4 +-
 libcxxabi/src/CMakeLists.txt                  |  1 -
 libcxxabi/src/demangle/DemangleConfig.h       |  8 ++--
 libcxxabi/src/log_error_and_continue.cpp      | 48 -------------------
 libcxxabi/src/log_error_and_continue.h        | 16 -------
 6 files changed, 15 insertions(+), 74 deletions(-)
 delete mode 100644 libcxxabi/src/log_error_and_continue.cpp
 delete mode 100644 libcxxabi/src/log_error_and_continue.h

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 2223d466c2a7f..ef0c8b48b658b 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -147,16 +147,20 @@ _LIBCPP_HARDENING_MODE_EXTENSIVE, \
 _LIBCPP_HARDENING_MODE_DEBUG
 #  endif
 
-// Hardening assertion semantics mirror the evaluation semantics of P3100 Contracts:
-// - `ignore` does not evaluate the assertion;
+// Hardening assertion semantics generally mirror the evaluation semantics of C++26 Contracts:
+// - `ignore` evaluates the assertion but doesn't do anything if it fails (note that it differs from the Contracts
+//   `ignore` semantic which wouldn't evaluate the assertion at all);
 // - `observe` logs an error (indicating, if possible, that the error is fatal) and continues execution;
 // - `quick-enforce` terminates the program as fast as possible (via trapping);
 // - `enforce` logs an error and then terminates the program.
+//
 // Notes:
 // - Continuing execution after a hardening check fails results in undefined behavior; the `observe` semantic is meant
 //   to make adopting hardening easier but should not be used outside of this scenario;
-// - P3471 "Standard Library Hardening" wording precludes using the Contracts `ignore` semantic for hardened
-//   preconditions in the Library; allowing this semantic to be used is a libc++ vendor extension.
+// - C++26 wording for Library Hardening precludes a conforming Hardened implementation from using the Contracts
+//   `ignore` semantic when evaluating hardened preconditions in the Library. Libc++ allows using this semantic for
+//   hardened preconditions, however, be aware that using `ignore` does not produce a conforming "Hardened"
+//   implementation, unlike the other semantics above.
 // clang-format off
 #  define _LIBCPP_ASSERTION_SEMANTIC_IGNORE        (1 << 1)
 #  define _LIBCPP_ASSERTION_SEMANTIC_OBSERVE       (1 << 2)
diff --git a/libcxx/vendor/llvm/default_assertion_handler.in b/libcxx/vendor/llvm/default_assertion_handler.in
index 3ec9a94790712..d1ddedf1bb31b 100644
--- a/libcxx/vendor/llvm/default_assertion_handler.in
+++ b/libcxx/vendor/llvm/default_assertion_handler.in
@@ -16,7 +16,7 @@
 #  include <__cxx03/__verbose_trap>
 #else
 #  include <__config>
-#  include <__log_hardening_failure>
+#  include <__log_error>
 #  include <__verbose_abort>
 #  include <__verbose_trap>
 #endif
@@ -29,7 +29,7 @@
 #  define _LIBCPP_ASSERTION_HANDLER(message) ((void)0)
 
 #elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
-#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_LOG_HARDENING_FAILURE(message)
+#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_LOG_ERROR(_LogErrorReason::_HardeningFailure, message)
 
 #elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
 #  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index 9a9a65ce3aaa1..0a6fc892a4f69 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -16,7 +16,6 @@ set(LIBCXXABI_SOURCES
   # Internal files
   abort_message.cpp
   fallback_malloc.cpp
-  log_error_and_continue.cpp
   private_typeinfo.cpp
 )
 
diff --git a/libcxxabi/src/demangle/DemangleConfig.h b/libcxxabi/src/demangle/DemangleConfig.h
index 994d59507588f..f1f161a54afbd 100644
--- a/libcxxabi/src/demangle/DemangleConfig.h
+++ b/libcxxabi/src/demangle/DemangleConfig.h
@@ -19,9 +19,11 @@
 #include "../abort_message.h"
 #endif
 
-#ifndef _LIBCPP_LOG_HARDENING_FAILURE
-#define _LIBCPP_LOG_HARDENING_FAILURE(message) __log_error_and_continue(message)
-#include "../log_error_and_continue.h"
+#ifndef _LIBCPP_LOG_ERROR
+// Libc++abi does not have any functionality to log and continue, so we drop error messages when we build the demangler
+// with `observe` assertion semantic. Once the layering with libc++ is improved, this could use the libc++ functionality
+// to log hardening failures.
+#define _LIBCPP_LOG_ERROR(reason, message) ((void)0)
 #endif
 
 #include <version>
diff --git a/libcxxabi/src/log_error_and_continue.cpp b/libcxxabi/src/log_error_and_continue.cpp
deleted file mode 100644
index 51088cd11d914..0000000000000
--- a/libcxxabi/src/log_error_and_continue.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include "log_error_and_continue.h"
-
-#ifdef __BIONIC__
-#  include <syslog.h>
-extern "C" void android_set_abort_message(const char* msg);
-#endif // __BIONIC__
-
-#if defined(__APPLE__) && __has_include(<os/reason_private.h>)
-#  include <os/reason_private.h>
-#   define _LIBCXXABI_USE_OS_FAULT
-#endif
-
-void __log_error_and_continue(const char* message)
-{
-  // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash.
-#if defined(_LIBCXXABI_USE_OS_FAULT)
-  os_fault_with_payload(
-      /*reason_namespace=*/OS_REASON_SECURITY,
-      /*reason_code=*/0,
-      /*payload=*/nullptr,
-      /*payload_size=*/0,
-      /*reason_string=*/message,
-      /*reason_flags=*/0);
-
-#elif defined(__BIONIC__)
-  // Show error in tombstone.
-  android_set_abort_message(message);
-
-  // Show error in logcat.
-  openlog("libc++", 0, 0);
-  syslog(LOG_CRIT, "%s", message);
-  closelog();
-
-#else
-  fprintf(stderr, "%s", message);
-#endif
-}
diff --git a/libcxxabi/src/log_error_and_continue.h b/libcxxabi/src/log_error_and_continue.h
deleted file mode 100644
index e976c1de65d06..0000000000000
--- a/libcxxabi/src/log_error_and_continue.h
+++ /dev/null
@@ -1,16 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef __LOG_ERROR_AND_CONTINUE_H_
-#define __LOG_ERROR_AND_CONTINUE_H_
-
-#include "cxxabi.h"
-
-extern "C" _LIBCXXABI_HIDDEN void __log_error_and_continue(const char* message);
-
-#endif // __LOG_ERROR_AND_CONTINUE_H_

>From 94ef9a34532637011ec831c321447b3566764fa8 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Sat, 12 Jul 2025 18:38:03 -0700
Subject: [PATCH 8/9] Test improvements

---
 libcxx/src/log_error.cpp                      |  6 ++---
 .../extents/assert.ctor_from_array.pass.cpp   | 21 +++++++++++-----
 .../assert.ctor_from_integral.pass.cpp        |  6 ++---
 .../layout_left/assert.conversion.pass.cpp    |  4 +--
 .../layout_left/assert.ctor.extents.pass.cpp  |  5 +++-
 .../assert.ctor.layout_right.pass.cpp         |  4 +--
 .../layout_right/assert.conversion.pass.cpp   |  6 ++---
 .../layout_right/assert.ctor.extents.pass.cpp |  3 ++-
 .../assert.ctor.layout_left.pass.cpp          |  4 +--
 .../layout_stride/assert.conversion.pass.cpp  | 18 +++++++------
 ...ert.ctor.extents_array.non_unique.pass.cpp |  4 +--
 .../assert.ctor.extents_array.pass.cpp        |  8 +++---
 ...sert.ctor.extents_span.non_unique.pass.cpp |  4 +--
 .../thread/thread.latch/assert.ctor.pass.cpp  |  9 ++++---
 .../thread.semaphore/assert.ctor.pass.cpp     |  2 +-
 .../assert.ctor.pass.cpp                      |  2 ++
 .../assert.ctor.pass.cpp                      |  2 ++
 .../assert.to_local.pass.cpp                  |  1 +
 .../time.zone.members/assert.to_sys.pass.cpp  |  2 ++
 .../assert.to_sys_choose.pass.cpp             |  2 ++
 libcxx/test/support/check_assertion.h         | 25 +++++++++++--------
 .../vendor/llvm/default_assertion_handler.in  |  2 +-
 22 files changed, 84 insertions(+), 56 deletions(-)

diff --git a/libcxx/src/log_error.cpp b/libcxx/src/log_error.cpp
index 53cf3c7aa7ac2..4e695ff380e8e 100644
--- a/libcxx/src/log_error.cpp
+++ b/libcxx/src/log_error.cpp
@@ -24,6 +24,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 namespace {
 
 void log_security_failure(const char* message) noexcept {
+  // Always log the message to `stderr` in case the platform-specific system calls fail.
+  fprintf(stderr, "%s", message);
+
   // On Apple platforms, use the `os_fault_with_payload` OS function that simulates a crash.
 #if defined(__APPLE__) && __has_include(<os/reason_private.h>)
   os_fault_with_payload(
@@ -42,9 +45,6 @@ void log_security_failure(const char* message) noexcept {
   openlog("libc++", 0, 0);
   syslog(LOG_CRIT, "%s", message);
   closelog();
-
-#else
-  fprintf(stderr, "%s", message);
 #endif
 }
 
diff --git a/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp
index 90cb0c84a063b..6849b6a5f2e6c 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp
@@ -43,18 +43,27 @@ int main(int, char**) {
   }
   // mismatch of static extent
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<int, D, 5> e1(std::array{1000, 3}); }()),
-                               "extents construction: mismatch of provided arguments with static extents.");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        ([] {
+          [[maybe_unused]] std::extents<int, D, 5> e1(std::array{1000, 3});
+        }()),
+        "extents construction: mismatch of provided arguments with static extents.");
   }
   // value out of range
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<signed char, D, 5> e1(std::array{1000, 5}); }()),
-                               "extents ctor: arguments must be representable as index_type and nonnegative");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        ([] {
+          [[maybe_unused]] std::extents<signed char, D, 5> e1(std::array{1000, 5});
+        }()),
+        "extents ctor: arguments must be representable as index_type and nonnegative");
   }
   // negative value
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<signed char, D, 5> e1(std::array{-1, 5}); }()),
-                               "extents ctor: arguments must be representable as index_type and nonnegative");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        ([] {
+          [[maybe_unused]] std::extents<signed char, D, 5> e1(std::array{-1, 5});
+        }()),
+        "extents ctor: arguments must be representable as index_type and nonnegative");
   }
   return 0;
 }
diff --git a/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp
index 37e79aabf8532..17bab03b922e6 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp
@@ -45,17 +45,17 @@ int main(int, char**) {
   }
   // mismatch of static extent
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<int, D, 5> e1(1000, 3); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([] { [[maybe_unused]] std::extents<int, D, 5> e1(1000, 3); }()),
                                "extents construction: mismatch of provided arguments with static extents.");
   }
   // value out of range
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<signed char, D, 5> e1(1000, 5); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([] { [[maybe_unused]] std::extents<signed char, D, 5> e1(1000, 5); }()),
                                "extents ctor: arguments must be representable as index_type and nonnegative");
   }
   // negative value
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<signed char, D, 5> e1(-1, 5); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([] { [[maybe_unused]] std::extents<signed char, D, 5> e1(-1, 5); }()),
                                "extents ctor: arguments must be representable as index_type and nonnegative");
   }
   return 0;
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.conversion.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.conversion.pass.cpp
index 7b6616f19d724..c67529671fcf7 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.conversion.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.conversion.pass.cpp
@@ -44,7 +44,7 @@ int main(int, char**) {
   {
     TEST_LIBCPP_ASSERT_FAILURE(
         ([=] {
-          std::layout_left::mapping<std::extents<signed char, D>> m(
+          [[maybe_unused]] std::layout_left::mapping<std::extents<signed char, D>> m(
               std::layout_left::mapping<std::extents<int, D>>(std::extents<int, D>(500)));
         }()),
         "extents ctor: arguments must be representable as index_type and nonnegative");
@@ -55,7 +55,7 @@ int main(int, char**) {
     [[maybe_unused]] std::extents<signed char, D, 5> e(arg_exts);
     // but the product is not, so we can't use it for layout_left
     TEST_LIBCPP_ASSERT_FAILURE(
-        ([=] { std::layout_left::mapping<std::extents<signed char, D, 5>> m(arg); }()),
+        ([=] { [[maybe_unused]] std::layout_left::mapping<std::extents<signed char, D, 5>> m(arg); }()),
         "layout_left::mapping converting ctor: other.required_span_size() must be representable as index_type.");
   }
   return 0;
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp
index 7c96f8ec9353f..45f07f1ed5c06 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp
@@ -31,7 +31,10 @@ int main(int, char**) {
   {
     // the extents are representable but the product is not, so we can't use it for layout_left
     TEST_LIBCPP_ASSERT_FAILURE(
-        ([=] { std::layout_left::mapping<std::extents<signed char, D, 5>> m(std::extents<signed char, D, 5>(100)); }()),
+        ([=] {
+          [[maybe_unused]] std::layout_left::mapping<std::extents<signed char, D, 5>> m(
+              std::extents<signed char, D, 5>(100));
+        }()),
         "layout_left::mapping extents ctor: product of extents must be representable as index_type.");
   }
   return 0;
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp
index e578bac2103b0..04a6c59d265e1 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp
@@ -39,14 +39,14 @@ int main(int, char**) {
   }
   // mismatch of static extent
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_left::mapping<std::extents<int, 3>> m(arg); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { [[maybe_unused]] std::layout_left::mapping<std::extents<int, 3>> m(arg); }()),
                                "extents construction: mismatch of provided arguments with static extents.");
   }
   // non-representability of extents itself
   {
     TEST_LIBCPP_ASSERT_FAILURE(
         ([=] {
-          std::layout_left::mapping<std::extents<signed char, D>> m(
+          [[maybe_unused]] std::layout_left::mapping<std::extents<signed char, D>> m(
               std::layout_right::mapping<std::extents<int, D>>(std::extents<int, D>(500)));
         }()),
         "extents ctor: arguments must be representable as index_type and nonnegative");
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.conversion.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.conversion.pass.cpp
index df16edb925407..81fc8b5d65e62 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.conversion.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.conversion.pass.cpp
@@ -37,14 +37,14 @@ int main(int, char**) {
   }
   // mismatch of static extent
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_right::mapping<std::extents<int, D, 3>> m(arg); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { [[maybe_unused]] std::layout_right::mapping<std::extents<int, D, 3>> m(arg); }()),
                                "extents construction: mismatch of provided arguments with static extents.");
   }
   // non-representability of extents itself
   {
     TEST_LIBCPP_ASSERT_FAILURE(
         ([=] {
-          std::layout_right::mapping<std::extents<signed char, D>> m(
+          [[maybe_unused]] std::layout_right::mapping<std::extents<signed char, D>> m(
               std::layout_right::mapping<std::extents<int, D>>(std::extents<int, D>(500)));
         }()),
         "extents ctor: arguments must be representable as index_type and nonnegative");
@@ -55,7 +55,7 @@ int main(int, char**) {
     [[maybe_unused]] std::extents<signed char, D, 5> e(arg_exts);
     // but the product is not, so we can't use it for layout_right
     TEST_LIBCPP_ASSERT_FAILURE(
-        ([=] { std::layout_right::mapping<std::extents<signed char, D, 5>> m(arg); }()),
+        ([=] { [[maybe_unused]] std::layout_right::mapping<std::extents<signed char, D, 5>> m(arg); }()),
         "layout_right::mapping converting ctor: other.required_span_size() must be representable as index_type.");
   }
   return 0;
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp
index 52095691f6d24..33b71fdfd052d 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp
@@ -32,7 +32,8 @@ int main(int, char**) {
     // the extents are representable but the product is not, so we can't use it for layout_right
     TEST_LIBCPP_ASSERT_FAILURE(
         ([=] {
-          std::layout_right::mapping<std::extents<signed char, D, 5>> m(std::extents<signed char, D, 5>(100));
+          [[maybe_unused]] std::layout_right::mapping<std::extents<signed char, D, 5>> m(
+              std::extents<signed char, D, 5>(100));
         }()),
         "layout_right::mapping extents ctor: product of extents must be representable as index_type.");
   }
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.layout_left.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.layout_left.pass.cpp
index 1757ddb286b9c..32972771f242d 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.layout_left.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.layout_left.pass.cpp
@@ -39,14 +39,14 @@ int main(int, char**) {
   }
   // mismatch of static extent
   {
-    TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_right::mapping<std::extents<int, 3>> m(arg); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { [[maybe_unused]] std::layout_right::mapping<std::extents<int, 3>> m(arg); }()),
                                "extents construction: mismatch of provided arguments with static extents.");
   }
   // non-representability of extents itself
   {
     TEST_LIBCPP_ASSERT_FAILURE(
         ([=] {
-          std::layout_right::mapping<std::extents<signed char, D>> m(
+          [[maybe_unused]] std::layout_right::mapping<std::extents<signed char, D>> m(
               std::layout_left::mapping<std::extents<int, D>>(std::extents<int, D>(500)));
         }()),
         "extents ctor: arguments must be representable as index_type and nonnegative");
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.conversion.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.conversion.pass.cpp
index 7deb1215de0de..7b1739e0d1031 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.conversion.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.conversion.pass.cpp
@@ -58,14 +58,14 @@ int main(int, char**) {
   {
     std::extents<int, D, D> arg_exts{100, 5};
     std::layout_stride::mapping<std::extents<int, D, D>> arg(arg_exts, std::array<int, 2>{1, 100});
-    TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_stride::mapping<std::extents<int, D, 3>> m(arg); }()),
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { [[maybe_unused]] std::layout_stride::mapping<std::extents<int, D, 3>> m(arg); }()),
                                "extents construction: mismatch of provided arguments with static extents.");
   }
   // non-representability of extents itself
   {
     TEST_LIBCPP_ASSERT_FAILURE(
         ([=] {
-          std::layout_stride::mapping<std::extents<signed char, D>> m(
+          [[maybe_unused]] std::layout_stride::mapping<std::extents<signed char, D>> m(
               std::layout_stride::mapping<std::extents<int, D>>(std::extents<int, D>(500), std::array<int, 1>{1}));
         }()),
         "extents ctor: arguments must be representable as index_type and nonnegative");
@@ -73,8 +73,9 @@ int main(int, char**) {
   // all strides must be larger than zero
   {
     always_convertible_layout::mapping<std::dextents<int, 2>> offset_map(std::dextents<int, 2>{10, 10}, 100, -1);
-    TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_stride::mapping<std::extents<signed char, D, D>> m(offset_map); }()),
-                               "layout_stride::mapping converting ctor: all strides must be greater than 0");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        ([=] { [[maybe_unused]] std::layout_stride::mapping<std::extents<signed char, D, D>> m(offset_map); }()),
+        "layout_stride::mapping converting ctor: all strides must be greater than 0");
   }
   // required_span_size not representable, while individual extents are
   {
@@ -84,7 +85,7 @@ int main(int, char**) {
     [[maybe_unused]] std::extents<signed char, D, 5> e(arg_exts);
     // but the product is not, so we can't use it for layout_stride
     TEST_LIBCPP_ASSERT_FAILURE(
-        ([=] { std::layout_stride::mapping<std::extents<signed char, D, 5>> m(arg); }()),
+        ([=] { [[maybe_unused]] std::layout_stride::mapping<std::extents<signed char, D, 5>> m(arg); }()),
         "layout_stride::mapping converting ctor: other.required_span_size() must be representable as index_type.");
   }
   // required_span_size not representable, while individual extents are, edge case
@@ -98,14 +99,15 @@ int main(int, char**) {
     [[maybe_unused]] std::extents<signed char, D, 10> e(arg_exts);
     // but the product is not, so we can't use it for layout_stride
     TEST_LIBCPP_ASSERT_FAILURE(
-        ([=] { std::layout_stride::mapping<std::extents<signed char, D, 10>> m(arg); }()),
+        ([=] { [[maybe_unused]] std::layout_stride::mapping<std::extents<signed char, D, 10>> m(arg); }()),
         "layout_stride::mapping converting ctor: other.required_span_size() must be representable as index_type.");
   }
   // base offset must be 0 (i.e. mapping(0,...,0)==0) for a strided layout with positive strides
   {
     always_convertible_layout::mapping<std::dextents<int, 2>> offset_map(std::dextents<int, 2>{10, 10}, 3);
-    TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_stride::mapping<std::extents<signed char, D, D>> m(offset_map); }()),
-                               "layout_stride::mapping converting ctor: base offset of mapping must be zero.");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        ([=] { [[maybe_unused]] std::layout_stride::mapping<std::extents<signed char, D, D>> m(offset_map); }()),
+        "layout_stride::mapping converting ctor: base offset of mapping must be zero.");
   }
   return 0;
 }
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.non_unique.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.non_unique.pass.cpp
index 97a6d56e4f839..a6120f8bf9c4b 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.non_unique.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.non_unique.pass.cpp
@@ -42,7 +42,7 @@ int main(int, char**) {
   {
     TEST_LIBCPP_ASSERT_FAILURE(
         ([=] {
-          std::layout_stride::mapping<std::extents<unsigned, D, 5, 7>> m(
+          [[maybe_unused]] std::layout_stride::mapping<std::extents<unsigned, D, 5, 7>> m(
               std::extents<unsigned, D, 5, 7>(20), std::array<unsigned, 3>{4, 1, 200});
         }()),
         "layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
@@ -58,7 +58,7 @@ int main(int, char**) {
     // will fail because neither of the equal strides is associated with an extent of 1
     TEST_LIBCPP_ASSERT_FAILURE(
         ([=] {
-          std::layout_stride::mapping<std::extents<unsigned, D, 5, 2>> m3(
+          [[maybe_unused]] std::layout_stride::mapping<std::extents<unsigned, D, 5, 2>> m3(
               std::extents<unsigned, D, 5, 2>(2), std::array<unsigned, 3>{5, 1, 5});
         }()),
         "layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.pass.cpp
index 860849ded2de2..9c52dd411b7c5 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.pass.cpp
@@ -42,7 +42,7 @@ int main(int, char**) {
   // the extents are representable but the product with strides is not, so we can't use it for layout_stride
   TEST_LIBCPP_ASSERT_FAILURE(
       ([=] {
-        std::layout_stride::mapping<std::extents<signed char, D, 5>> m(
+        [[maybe_unused]] std::layout_stride::mapping<std::extents<signed char, D, 5>> m(
             std::extents<signed char, D, 5>(20), std::array<int, 2>{20, 1});
       }()),
       "layout_stride::mapping ctor: required span size is not representable as index_type.");
@@ -51,7 +51,7 @@ int main(int, char**) {
   static_assert(static_cast<unsigned char>(257u) == 1);
   TEST_LIBCPP_ASSERT_FAILURE(
       ([=] {
-        std::layout_stride::mapping<std::extents<unsigned char, D, 5>> m(
+        [[maybe_unused]] std::layout_stride::mapping<std::extents<unsigned char, D, 5>> m(
             std::extents<unsigned char, D, 5>(20), std::array<unsigned, 2>{257, 1});
       }()),
       "layout_stride::mapping ctor: required span size is not representable as index_type.");
@@ -59,14 +59,14 @@ int main(int, char**) {
   // negative strides are not allowed, check with unsigned index_type so we make sure we catch that
   TEST_LIBCPP_ASSERT_FAILURE(
       ([=] {
-        std::layout_stride::mapping<std::extents<unsigned, D, 5>> m(
+        [[maybe_unused]] std::layout_stride::mapping<std::extents<unsigned, D, 5>> m(
             std::extents<unsigned, D, 5>(20), std::array<int, 2>{20, -1});
       }()),
       "layout_stride::mapping ctor: all strides must be greater than 0");
   // zero strides are not allowed, check with unsigned index_type so we make sure we catch that
   TEST_LIBCPP_ASSERT_FAILURE(
       ([=] {
-        std::layout_stride::mapping<std::extents<unsigned, D, 5>> m(
+        [[maybe_unused]] std::layout_stride::mapping<std::extents<unsigned, D, 5>> m(
             std::extents<unsigned, D, 5>(20), std::array<unsigned, 2>{20, 0});
       }()),
       "layout_stride::mapping ctor: all strides must be greater than 0");
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.non_unique.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.non_unique.pass.cpp
index fd0701e9ee3a7..4752d3946aece 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.non_unique.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.non_unique.pass.cpp
@@ -43,7 +43,7 @@ int main(int, char**) {
     TEST_LIBCPP_ASSERT_FAILURE(
         ([=] {
           std::array<unsigned, 3> strides{4, 1, 200};
-          std::layout_stride::mapping<std::extents<unsigned, D, 5, 7>> m(
+          [[maybe_unused]] std::layout_stride::mapping<std::extents<unsigned, D, 5, 7>> m(
               std::extents<unsigned, D, 5, 7>(20), std::span(strides));
         }()),
         "layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
@@ -61,7 +61,7 @@ int main(int, char**) {
     // will fail because neither of the equal strides is associated with an extent of 1
     TEST_LIBCPP_ASSERT_FAILURE(
         ([=] {
-          std::layout_stride::mapping<std::extents<unsigned, D, 5, 2>> m3(
+          [[maybe_unused]] std::layout_stride::mapping<std::extents<unsigned, D, 5, 2>> m3(
               std::extents<unsigned, D, 5, 2>(2), std::span(strides));
         }()),
         "layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
diff --git a/libcxx/test/libcxx/thread/thread.latch/assert.ctor.pass.cpp b/libcxx/test/libcxx/thread/thread.latch/assert.ctor.pass.cpp
index 5f1ea19d82a50..01fb907c3fbd4 100644
--- a/libcxx/test/libcxx/thread/thread.latch/assert.ctor.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.latch/assert.ctor.pass.cpp
@@ -24,11 +24,12 @@
 
 #include "check_assertion.h"
 
-int main(int, char **) {
+int main(int, char**) {
   {
-    TEST_LIBCPP_ASSERT_FAILURE([]{ std::latch l(-1); }(),
-                               "latch::latch(ptrdiff_t): latch cannot be "
-                               "initialized with a negative value");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        [] { [[maybe_unused]] std::latch l(-1); }(),
+        "latch::latch(ptrdiff_t): latch cannot be "
+        "initialized with a negative value");
   }
 
   // We can't check the precondition for max() because there's no value
diff --git a/libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp b/libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp
index 1e33add779496..c490948d36abe 100644
--- a/libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp
@@ -26,7 +26,7 @@
 int main(int, char**) {
   {
     TEST_LIBCPP_ASSERT_FAILURE(
-        [] { std::counting_semaphore<> s(-1); }(),
+        [] { [[maybe_unused]] std::counting_semaphore<> s(-1); }(),
         "counting_semaphore::counting_semaphore(ptrdiff_t): counting_semaphore cannot be "
         "initialized with a negative value");
   }
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp
index 73e6bf2846f0e..ee2370f5bce25 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp
@@ -7,12 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
 
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 // XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
 
 // <chrono>
 
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp
index fdd9f79958f98..5d896c34e4ccd 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp
@@ -7,12 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
 
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 // XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
 
 // <chrono>
 
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_local.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_local.pass.cpp
index d9ca1c80751cc..eb0ae4cf4b187 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_local.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_local.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
 
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp
index 3a2ff00088676..57b7f8d0f30a0 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp
@@ -7,12 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
 
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 // XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
 
 // <chrono>
 
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys_choose.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys_choose.pass.cpp
index 65429345ae794..85ce6019fcd55 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys_choose.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys_choose.pass.cpp
@@ -7,12 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
 
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 // XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
 
 // <chrono>
 
diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h
index 7f8102d82970d..d77c0bdb3615e 100644
--- a/libcxx/test/support/check_assertion.h
+++ b/libcxx/test/support/check_assertion.h
@@ -44,10 +44,18 @@ static constexpr const char* Marker = "###";
 using MatchResult = std::pair<bool, std::string>;
 using Matcher     = std::function<MatchResult(const std::string& /*text*/)>;
 
-MatchResult MatchAssertionMessage(const std::string& text, std::string_view expected_message) {
+// Using the marker makes matching more precise, but we cannot output the marker when the `observe` semantic is used
+// (because it doesn't allow customizing the logging function). If the marker is not available, fall back to using less
+// precise matching by just the error message.
+MatchResult MatchAssertionMessage(const std::string& text, std::string_view expected_message, bool use_marker) {
   // Extract information from the error message. This has to stay synchronized with how we format assertions in the
   // library.
-  std::regex assertion_format(".*###\\n(.*):(\\d+): assertion (.*) failed: (.*)\\n###");
+  std::string assertion_format_string = [&] {
+    if (use_marker)
+      return (".*###\\n(.*):(\\d+): assertion (.*) failed: (.*)\\n###");
+    return ("(.*):(\\d+): assertion (.*) failed: (.*)\\n");
+  }();
+  std::regex assertion_format(assertion_format_string);
 
   std::smatch match_result;
   // If a non-terminating assertion semantic is used, more than one assertion might be triggered before the process
@@ -57,6 +65,7 @@ MatchResult MatchAssertionMessage(const std::string& text, std::string_view expe
     std::stringstream matching_error;
     matching_error                                                     //
         << "Failed to parse the assertion message.\n"                  //
+        << "Using marker: "        << use_marker << "\n"               //
         << "Expected message:   '" << expected_message.data() << "'\n" //
         << "Stderr contents:    '" << text.c_str() << "'\n";
     return MatchResult(/*success=*/false, matching_error.str());
@@ -80,9 +89,9 @@ MatchResult MatchAssertionMessage(const std::string& text, std::string_view expe
   return MatchResult(/*success=*/true, /*maybe_error=*/"");
 }
 
-Matcher MakeAssertionMessageMatcher(std::string_view assertion_message) {
+Matcher MakeAssertionMessageMatcher(std::string_view assertion_message, bool use_marker = true) {
   return [=](const std::string& text) { //
-    return MatchAssertionMessage(text, assertion_message);
+    return MatchAssertionMessage(text, assertion_message, use_marker);
   };
 }
 
@@ -389,12 +398,6 @@ class DeathTest {
 };
 
 #ifdef _LIBCPP_VERSION
-void std::__libcpp_log_hardening_failure(const char* message) noexcept {
-  std::fprintf(stderr, "%s\n", Marker);
-  std::fprintf(stderr, "%s", message);
-  std::fprintf(stderr, "%s\n", Marker);
-}
-
 void std::__libcpp_verbose_abort(char const* format, ...) noexcept {
   va_list args;
   va_start(args, format);
@@ -479,7 +482,7 @@ bool ExpectLog(const char* stmt, Func&& func) {
     assert(( ExpectDeath(DeathCause::Trap,         #expr, [&]() { (void)(expr); }) ))
 #elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
 #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
-    assert(( ExpectLog(#expr, [&]() { (void)(expr); }, MakeAssertionMessageMatcher(message)) ))
+    assert(( ExpectLog(#expr, [&]() { (void)(expr); }, MakeAssertionMessageMatcher(message, /*use_marker=*/false)) ))
 #elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
 #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
     assert(( ExpectLog(#expr, [&]() { (void)(expr); }) ))
diff --git a/libcxx/vendor/llvm/default_assertion_handler.in b/libcxx/vendor/llvm/default_assertion_handler.in
index d1ddedf1bb31b..e113a1b82cdd2 100644
--- a/libcxx/vendor/llvm/default_assertion_handler.in
+++ b/libcxx/vendor/llvm/default_assertion_handler.in
@@ -29,7 +29,7 @@
 #  define _LIBCPP_ASSERTION_HANDLER(message) ((void)0)
 
 #elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
-#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_LOG_ERROR(_LogErrorReason::_HardeningFailure, message)
+#  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_LOG_ERROR(::std::_LogErrorReason::_HardeningFailure, message)
 
 #elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
 #  define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)

>From 0993a0822e8fe60b1822eeab4ac67e0467a70bde Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Sat, 12 Jul 2025 19:18:26 -0700
Subject: [PATCH 9/9] [libc++] Add missing unsupported attributes to hardening
 timezone tests.

Before this patch, these tests fail under `extensive` and `debug`
hardening modes.
---
 .../time.zone.exception.ambig/assert.ctor.pass.cpp              | 2 ++
 .../time.zone.exception.nonexist/assert.ctor.pass.cpp           | 2 ++
 .../time.zone.members/assert.to_local.pass.cpp                  | 1 +
 .../time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp | 2 ++
 .../time.zone.members/assert.to_sys_choose.pass.cpp             | 2 ++
 5 files changed, 9 insertions(+)

diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp
index 73e6bf2846f0e..ee2370f5bce25 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp
@@ -7,12 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
 
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 // XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
 
 // <chrono>
 
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp
index fdd9f79958f98..5d896c34e4ccd 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp
@@ -7,12 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
 
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 // XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
 
 // <chrono>
 
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_local.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_local.pass.cpp
index d9ca1c80751cc..eb0ae4cf4b187 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_local.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_local.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
 
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp
index 3a2ff00088676..57b7f8d0f30a0 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp
@@ -7,12 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
 
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 // XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
 
 // <chrono>
 
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys_choose.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys_choose.pass.cpp
index 65429345ae794..85ce6019fcd55 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys_choose.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys_choose.pass.cpp
@@ -7,12 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
 
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 // XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
 
 // <chrono>
 



More information about the libcxx-commits mailing list