[libcxx-commits] [libcxx] [libc++] Add std::stacktrace (P0881R7) (PR #136528)

Steve O'Brien via libcxx-commits libcxx-commits at lists.llvm.org
Mon Oct 27 14:31:17 PDT 2025


https://github.com/elsteveogrande updated https://github.com/llvm/llvm-project/pull/136528

>From 1059993ee30a63fe868f9abe11affe42dfa54316 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Thu, 16 Jan 2025 18:32:09 -0500
Subject: [PATCH 1/9] Add C++23 stacktrace

---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/22.rst               |   2 +
 libcxx/docs/Status/Cxx23Issues.csv            |   2 +-
 libcxx/docs/Status/Cxx23Papers.csv            |   6 +-
 libcxx/include/CMakeLists.txt                 |   3 +
 libcxx/include/__config                       |  27 ++
 libcxx/include/__configuration/availability.h |   4 +
 .../include/__stacktrace/basic_stacktrace.h   | 342 ++++++++++++++++++
 .../include/__stacktrace/stacktrace_entry.h   | 211 +++++++++++
 libcxx/include/module.modulemap.in            |  25 ++
 libcxx/include/stacktrace                     | 187 ++++++++++
 libcxx/include/version                        |   4 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  11 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  11 +-
 ...xxabi.v1.stable.noexceptions.nonew.abilist |  11 +-
 libcxx/modules/std.compat.cppm.in             |   3 -
 libcxx/modules/std.cppm.in                    |   4 +-
 libcxx/modules/std/stacktrace.inc             |  14 +-
 libcxx/src/CMakeLists.txt                     |   5 +
 libcxx/src/stacktrace/images.cpp              |  92 +++++
 libcxx/src/stacktrace/images.h                | 119 ++++++
 libcxx/src/stacktrace/impl.cpp                |  84 +++++
 libcxx/src/stacktrace/impl_generic.cpp        |  91 +++++
 libcxx/src/stacktrace/impl_windows.cpp        | 248 +++++++++++++
 libcxx/src/stacktrace/unwinding.h             | 108 ++++++
 .../test/libcxx/transitive_includes/cxx23.csv |  21 ++
 .../test/libcxx/transitive_includes/cxx26.csv |  21 ++
 .../stacktrace/basic.cmp/equality.pass.cpp    |  59 +++
 .../basic.cmp/strong_ordering.pass.cpp        |  89 +++++
 .../assert.current.no_overflow.pass.cpp       |  30 ++
 .../stacktrace/basic.cons/copy.pass.cpp       |  81 +++++
 .../basic.cons/ctor_no_args.pass.cpp          |  51 +++
 .../basic.cons/ctor_with_alloc.pass.cpp       |  45 +++
 .../basic.cons/current_no_args.pass.cpp       |  67 ++++
 .../basic.cons/current_skip.pass.cpp          |  46 +++
 .../basic.cons/current_skip_depth.pass.cpp    |  58 +++
 .../stacktrace/basic.cons/move.pass.cpp       |  89 +++++
 .../stacktrace/basic.hash.pass.cpp            |  38 ++
 .../stacktrace/basic.mod/swap.pass.cpp        |  65 ++++
 .../basic.nonmem/operator_left_shift.pass.cpp |  44 +++
 .../stacktrace/basic.nonmem/swap.pass.cpp     |  58 +++
 .../basic.nonmem/to_string.pass.cpp           |  45 +++
 .../stacktrace/basic.obs/at.pass.cpp          |  49 +++
 .../stacktrace/basic.obs/begin_end.pass.cpp   |  44 +++
 .../stacktrace/basic.obs/cbegin_cend.pass.cpp |  39 ++
 .../basic.obs/crbegin_crend.pass.cpp          |  39 ++
 .../stacktrace/basic.obs/empty.pass.cpp       |  34 ++
 .../basic.obs/get_allocator.pass.cpp          |  27 ++
 .../stacktrace/basic.obs/max_size.pass.cpp    |  33 ++
 .../basic.obs/operator_index.pass.cpp         |  48 +++
 .../stacktrace/basic.obs/rbegin_rend.pass.cpp |  40 ++
 .../stacktrace/basic.obs/size.pass.cpp        |  34 ++
 .../std/diagnostics/stacktrace/basic.pass.cpp | 143 ++++++++
 .../stacktrace/entry.cmp/equality.pass.cpp    |  47 +++
 .../entry.cmp/strong_ordering.pass.cpp        |  56 +++
 .../entry.cons/copy_assign.pass.cpp           |  37 ++
 .../entry.cons/copy_construct.pass.cpp        |  36 ++
 .../stacktrace/entry.cons/default.pass.cpp    |  34 ++
 .../entry.obs/native_handle.pass.cpp          |  29 ++
 .../entry.obs/operator_bool.pass.cpp          |  37 ++
 .../std/diagnostics/stacktrace/entry.pass.cpp |  62 ++++
 .../entry.query/description.pass.cpp          |  32 ++
 .../entry.query/source_file.pass.cpp          |  32 ++
 .../entry.query/source_line.pass.cpp          |  30 ++
 .../stacktrace/only_uses_allocator.pass.cpp   | 115 ++++++
 .../diagnostics/stacktrace/simple.pass.cpp    |  33 ++
 .../std/diagnostics/stacktrace/test_allocs.h  | 106 ++++++
 .../stacktrace.version.compile.pass.cpp       | 119 ++++++
 .../version.version.compile.pass.cpp          |   8 +-
 libcxx/test/support/test_macros.h             |  18 +
 .../generate_feature_test_macro_components.py |   3 +-
 libcxx/utils/libcxx/header_information.py     |   3 +-
 libcxx/utils/libcxx/test/features.py          |   8 +
 73 files changed, 3871 insertions(+), 27 deletions(-)
 create mode 100644 libcxx/include/__stacktrace/basic_stacktrace.h
 create mode 100644 libcxx/include/__stacktrace/stacktrace_entry.h
 create mode 100644 libcxx/include/stacktrace
 create mode 100644 libcxx/src/stacktrace/images.cpp
 create mode 100644 libcxx/src/stacktrace/images.h
 create mode 100644 libcxx/src/stacktrace/impl.cpp
 create mode 100644 libcxx/src/stacktrace/impl_generic.cpp
 create mode 100644 libcxx/src/stacktrace/impl_windows.cpp
 create mode 100644 libcxx/src/stacktrace/unwinding.h
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.query/description.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.query/source_file.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.query/source_line.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/simple.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/test_allocs.h
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 8fba6db871f08..d0fbcd389760c 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -396,7 +396,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_spanstream``                                   *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_stacktrace``                                   *unimplemented*
+    ``__cpp_lib_stacktrace``                                   ``202011L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_stdatomic_h``                                  ``202011L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 25d33a9c2eb50..e1b252b696fef 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -46,6 +46,8 @@ Implemented Papers
 - P2835R7: Expose ``std::atomic_ref``'s object address (`Github <https://llvm.org/PR118377>`__)
 - P2944R3: Comparisons for ``reference_wrapper`` (`Github <https://llvm.org/PR105424>`__)
 - P3168R2: Give ``std::optional`` Range Support (`Github <https://llvm.org/PR105430>`__)
+- P0881R7: A Proposal to add stacktrace library (`Github <https://github.com/llvm/llvm-project/issues/105131>`__)
+- P2301R1: Add a `pmr` alias for `std::stacktrace` (`Github <https://github.com/llvm/llvm-project/issues/105167>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 5a68b51ec85fb..42419a5345eb1 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -189,7 +189,7 @@
 "`LWG3028 <https://wg21.link/LWG3028>`__","Container requirements tables should distinguish ``const`` and non-``const`` variables","2022-11 (Kona)","","","`#104995 <https://github.com/llvm/llvm-project/issues/104995>`__",""
 "`LWG3118 <https://wg21.link/LWG3118>`__","``fpos`` equality comparison unspecified","2022-11 (Kona)","","","`#104996 <https://github.com/llvm/llvm-project/issues/104996>`__",""
 "`LWG3177 <https://wg21.link/LWG3177>`__","Limit permission to specialize variable templates to program-defined types","2022-11 (Kona)","|Nothing To Do|","","`#104997 <https://github.com/llvm/llvm-project/issues/104997>`__",""
-"`LWG3515 <https://wg21.link/LWG3515>`__","§[stacktrace.basic.nonmem]: ``operator<<`` should be less templatized","2022-11 (Kona)","","","`#104998 <https://github.com/llvm/llvm-project/issues/104998>`__",""
+"`LWG3515 <https://wg21.link/LWG3515>`__","§[stacktrace.basic.nonmem]: ``operator<<`` should be less templatized","2022-11 (Kona)","|Complete|","","`#104998 <https://github.com/llvm/llvm-project/issues/104998>`__",""
 "`LWG3545 <https://wg21.link/LWG3545>`__","``std::pointer_traits`` should be SFINAE-friendly","2022-11 (Kona)","|Complete|","18","`#104999 <https://github.com/llvm/llvm-project/issues/104999>`__",""
 "`LWG3569 <https://wg21.link/LWG3569>`__","``join_view`` fails to support ranges of ranges with non-default_initializable iterators","2022-11 (Kona)","","","`#105000 <https://github.com/llvm/llvm-project/issues/105000>`__",""
 "`LWG3594 <https://wg21.link/LWG3594>`__","``inout_ptr`` — inconsistent ``release()`` in destructor","2022-11 (Kona)","|Complete|","19","`#105001 <https://github.com/llvm/llvm-project/issues/105001>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index b655384bad7f2..f1f5e1ebc2ee2 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -1,5 +1,5 @@
 "Paper #","Paper Name","Meeting","Status","First released version","GitHub issue","Notes"
-"`P0881R7 <https://wg21.link/P0881R7>`__","A Proposal to add stacktrace library","2020-11 (Virtual)","","","`#105131 <https://github.com/llvm/llvm-project/issues/105131>`__",""
+"`P0881R7 <https://wg21.link/P0881R7>`__","A Proposal to add stacktrace library","2020-11 (Virtual)","|Complete|","22","`#105131 <https://github.com/llvm/llvm-project/issues/105131>`__",""
 "`P0943R6 <https://wg21.link/P0943R6>`__","Support C atomics in C++","2020-11 (Virtual)","|Complete|","15","`#105132 <https://github.com/llvm/llvm-project/issues/105132>`__",""
 "`P1048R1 <https://wg21.link/P1048R1>`__","A proposal for a type trait to detect scoped enumerations","2020-11 (Virtual)","|Complete|","12","`#105134 <https://github.com/llvm/llvm-project/issues/105134>`__",""
 "`P1679R3 <https://wg21.link/P1679R3>`__","string contains function","2020-11 (Virtual)","|Complete|","12","`#105135 <https://github.com/llvm/llvm-project/issues/105135>`__",""
@@ -32,7 +32,7 @@
 "`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","","`#105164 <https://github.com/llvm/llvm-project/issues/105164>`__",""
 "`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","","","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
 "`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14","`#105166 <https://github.com/llvm/llvm-project/issues/105166>`__",""
-"`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
+"`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","|Complete|","22","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
 "`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|In Progress|","","`#105169 <https://github.com/llvm/llvm-project/issues/105169>`__",""
 "`P2340R1 <https://wg21.link/P2340R1>`__","Clarifying the status of the 'C headers'","2021-10 (Virtual)","|Nothing To Do|","","`#105170 <https://github.com/llvm/llvm-project/issues/105170>`__",""
 "`P2393R1 <https://wg21.link/P2393R1>`__","Cleaning up ``integer``-class types","2021-10 (Virtual)","","","`#105171 <https://github.com/llvm/llvm-project/issues/105171>`__",""
@@ -110,7 +110,7 @@
 "`P2713R1 <https://wg21.link/P2713R1>`__","Escaping improvements in ``std::format``","2023-02 (Issaquah)","|Complete|","19","`#105254 <https://github.com/llvm/llvm-project/issues/105254>`__",""
 "`P2675R1 <https://wg21.link/P2675R1>`__","``format``'s width estimation is too approximate and not forward compatible","2023-02 (Issaquah)","|Complete|","17","`#105255 <https://github.com/llvm/llvm-project/issues/105255>`__",""
 "`P2572R1 <https://wg21.link/P2572R1>`__","``std::format`` fill character allowances","2023-02 (Issaquah)","|Complete|","17","`#105256 <https://github.com/llvm/llvm-project/issues/105256>`__",""
-"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``","2023-02 (Issaquah)","|Partial|","","`#105257 <https://github.com/llvm/llvm-project/issues/105257>`__","The formatter for ``stacktrace`` is not implemented, since ``stacktrace`` is not implemented yet"
+"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``","2023-02 (Issaquah)","|Partial|","22","`#105257 <https://github.com/llvm/llvm-project/issues/105257>`__",""
 "`P2679R2 <https://wg21.link/P2679R2>`__","Fixing ``std::start_lifetime_as`` for arrays","2023-02 (Issaquah)","","","`#105258 <https://github.com/llvm/llvm-project/issues/105258>`__",""
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 37259a7e6e7dd..327fb28541b24 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -741,6 +741,8 @@ set(files
   __ranges/zip_transform_view.h
   __ranges/zip_view.h
   __split_buffer
+  __stacktrace/basic_stacktrace.h
+  __stacktrace/stacktrace_entry.h
   __std_mbstate_t.h
   __stop_token/atomic_unique_lock.h
   __stop_token/intrusive_list_view.h
@@ -1062,6 +1064,7 @@ set(files
   span
   sstream
   stack
+  stacktrace
   stdatomic.h
   stdbool.h
   stddef.h
diff --git a/libcxx/include/__config b/libcxx/include/__config
index b4c081dcdff1b..4d32f1fba03c7 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -965,6 +965,33 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_NOINLINE
 #  endif
 
+// Some functions, e.g. std::stacktrace::current, need to avoid being
+// tail-called by (and tail-calling other) functions, for proper enumeration of
+// call-stack frames.
+// clang-format off
+
+// Disables tail-call optimization for "outbound" calls
+// performed in the function annotated with this attribute.
+#  if __has_cpp_attribute(_Clang::__disable_tail_calls__)
+#    define _LIBCPP_NO_TAIL_CALLS_OUT [[_Clang::__disable_tail_calls__]]
+#  elif __has_cpp_attribute(__gnu__::__optimize__)
+#    define _LIBCPP_NO_TAIL_CALLS_OUT [[__gnu__::__optimize__("no-optimize-sibling-calls")]]
+#  else
+#    define _LIBCPP_NO_TAIL_CALLS_OUT
+#  endif
+
+// Disables tail-call optimization for "inbound" calls -- that is,
+// calls from some other function calling the one having this attribute.
+#  if __has_cpp_attribute(_Clang::__not_tail_called__)
+#    define _LIBCPP_NO_TAIL_CALLS_IN [[_Clang::__not_tail_called__]]
+#  else
+#    define _LIBCPP_NO_TAIL_CALLS_IN
+#  endif
+
+// Disable TCO for calls into, and out from, the annotated function.
+#  define _LIBCPP_NO_TAIL_CALLS _LIBCPP_NO_TAIL_CALLS_IN _LIBCPP_NO_TAIL_CALLS_OUT
+// clang-format on
+
 // We often repeat things just for handling wide characters in the library.
 // When wide characters are disabled, it can be useful to have a quick way of
 // disabling it without having to resort to #if-#endif, which has a larger
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index d0414ecfac2bb..d022922b78b36 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -291,6 +291,10 @@
 #define _LIBCPP_AVAILABILITY_HAS_BAD_FUNCTION_CALL_GOOD_WHAT_MESSAGE _LIBCPP_INTRODUCED_IN_LLVM_21
 // No attribute, since we've had bad_function_call::what() in the headers before
 
+// This controls the availability of C++23 <stacktrace>.
+#define _LIBCPP_AVAILABILITY_HAS_STACKTRACE _LIBCPP_INTRODUCED_IN_LLVM_21
+#define _LIBCPP_AVAILABILITY_STACKTRACE _LIBCPP_INTRODUCED_IN_LLVM_21_ATTRIBUTE
+
 // Define availability attributes that depend on both
 // _LIBCPP_HAS_EXCEPTIONS and _LIBCPP_HAS_RTTI.
 #if !_LIBCPP_HAS_EXCEPTIONS || !_LIBCPP_HAS_RTTI
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
new file mode 100644
index 0000000000000..291ca672631b1
--- /dev/null
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -0,0 +1,342 @@
+// -*- 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_STACKTRACE_BASIC
+#define _LIBCPP_STACKTRACE_BASIC
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#include <__assert>
+#include <__cstddef/size_t.h>
+#include <__functional/function.h>
+#include <__functional/hash.h>
+#include <__iterator/iterator.h>
+#include <__iterator/reverse_iterator.h>
+#include <__memory/allocator_traits.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__new/allocate.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__vector/vector.h>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <utility>
+
+#if _LIBCPP_HAS_LOCALIZATION
+#  include <__fwd/format.h>
+#  include <__fwd/ostream.h>
+#endif // _LIBCPP_HAS_LOCALIZATION
+
+#if _LIBCPP_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+
+#  include <__stacktrace/stacktrace_entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __stacktrace {
+
+template <typename _Tp, typename _Bp = _Tp>
+struct iters {
+  _Tp* __data_{};
+  size_t __size_{};
+
+  _Bp* data() { return (_Bp*)__data_; }
+  size_t size() const { return __size_; }
+  _Bp* begin() { return data(); }
+  _Bp* end() { return data() + size(); }
+};
+
+struct base {
+  constexpr static size_t __default_max_depth  = 64;
+  constexpr static size_t __absolute_max_depth = 256;
+
+  str_alloc<char> __string_alloc_;
+
+  using _EntryIters _LIBCPP_NODEBUG = iters<stacktrace_entry, entry_base>;
+  function<_EntryIters()> __entry_iters_;
+  function<entry_base&()> __entry_append_;
+
+  template <class _Allocator>
+  _LIBCPP_HIDE_FROM_ABI
+  base(_Allocator const& __alloc, function<_EntryIters()> __entry_iters, function<entry_base&()> __entry_append)
+      : __string_alloc_(std::move(str_alloc<char>::make(__alloc))),
+        __entry_iters_(__entry_iters),
+        __entry_append_(__entry_append) {}
+
+  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void current_impl(size_t __skip, size_t __max_depth);
+
+  _LIBCPP_HIDE_FROM_ABI void find_images();
+  _LIBCPP_HIDE_FROM_ABI void find_symbols();
+  _LIBCPP_HIDE_FROM_ABI void find_source_locs();
+
+  _LIBCPP_EXPORTED_FROM_ABI ostream& write_to(ostream& __os) const;
+  _LIBCPP_EXPORTED_FROM_ABI string to_string() const;
+
+  _LIBCPP_HIDE_FROM_ABI str __create_str() { return str(__string_alloc_); }
+};
+
+} // namespace __stacktrace
+
+// (19.6.4)
+// Class template basic_stacktrace [stacktrace.basic]
+
+class stacktrace_entry;
+
+template <class _Allocator>
+class basic_stacktrace : private __stacktrace::base {
+  friend struct hash<basic_stacktrace<_Allocator>>;
+  friend struct __stacktrace::base;
+
+  using _ATraits _LIBCPP_NODEBUG            = allocator_traits<_Allocator>;
+  constexpr static bool __kPropOnCopyAssign = _ATraits::propagate_on_container_copy_assignment::value;
+  constexpr static bool __kPropOnMoveAssign = _ATraits::propagate_on_container_move_assignment::value;
+  constexpr static bool __kPropOnSwap       = _ATraits::propagate_on_container_swap::value;
+  constexpr static bool __kAlwaysEqual      = _ATraits::is_always_equal::value;
+  constexpr static bool __kNoThrowAlloc     = noexcept(noexcept(_Allocator().allocate(1)));
+
+  _LIBCPP_NO_UNIQUE_ADDRESS _Allocator __alloc_;
+
+  vector<stacktrace_entry, _Allocator> __entries_;
+  _LIBCPP_HIDE_FROM_ABI _EntryIters entry_iters() { return {__entries_.data(), __entries_.size()}; }
+  _LIBCPP_HIDE_FROM_ABI __stacktrace::entry_base& entry_append() {
+    return (__stacktrace::entry_base&)__entries_.emplace_back();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI auto entry_iters_fn() {
+    return [this] -> _EntryIters { return entry_iters(); };
+  }
+  _LIBCPP_HIDE_FROM_ABI auto entry_append_fn() {
+    return [this] -> __stacktrace::entry_base& { return entry_append(); };
+  }
+
+public:
+  // (19.6.4.1)
+  // Overview [stacktrace.basic.overview]
+
+  using value_type             = stacktrace_entry;
+  using const_reference        = value_type const&;
+  using reference              = value_type&;
+  using difference_type        = ptrdiff_t;
+  using size_type              = size_t;
+  using allocator_type         = _Allocator;
+  using iterator               = decltype(__entries_.begin());
+  using const_iterator         = decltype(__entries_.cbegin());
+  using reverse_iterator       = decltype(__entries_.rbegin());
+  using const_reverse_iterator = decltype(__entries_.crbegin());
+
+  // (19.6.4.2)
+  // Creation and assignment [stacktrace.basic.cons]
+
+  _LIBCPP_ALWAYS_INLINE static basic_stacktrace
+  current(const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+    size_type __skip      = 0;
+    size_type __max_depth = __default_max_depth;
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
+    basic_stacktrace __ret{__caller_alloc};
+    __ret.current_impl(__skip, __max_depth);
+    return __ret;
+  }
+
+  _LIBCPP_ALWAYS_INLINE static basic_stacktrace
+  current(size_type __skip, const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+    size_type __max_depth = __default_max_depth;
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
+    basic_stacktrace __ret{__caller_alloc};
+    __ret.current_impl(__skip, __max_depth);
+    return __ret;
+  }
+
+  _LIBCPP_ALWAYS_INLINE static basic_stacktrace
+  current(size_type __skip,
+          size_type __max_depth,
+          const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
+    basic_stacktrace __ret{__caller_alloc};
+    if (__max_depth) [[likely]] {
+      __ret.current_impl(__skip, __max_depth);
+    }
+    return __ret;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr ~basic_stacktrace() = default;
+
+  static_assert(sizeof(__stacktrace::entry_base) == sizeof(stacktrace_entry));
+
+  _LIBCPP_HIDE_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc)
+      : base(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__alloc_) {}
+
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
+      : base(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__other.__entries_) {}
+
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
+      : base(__alloc, entry_iters_fn(), entry_append_fn()),
+        __alloc_(__alloc),
+        __entries_(std::move(__other.__entries_)) {}
+
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>)
+      : basic_stacktrace(allocator_type()) {}
+
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other) noexcept
+      : basic_stacktrace(__other, _ATraits::select_on_container_copy_construction(__other.__alloc_)) {}
+
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept
+      : basic_stacktrace(std::move(__other), __other.__alloc_) {}
+
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace& operator=(const basic_stacktrace& __other) {
+    if (std::addressof(__other) != this) {
+      if (__kPropOnCopyAssign) {
+        new (this) basic_stacktrace(__other, __other.__alloc_);
+      } else {
+        new (this) basic_stacktrace(__other);
+      }
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace&
+  operator=(basic_stacktrace&& __other) noexcept(__kPropOnMoveAssign || __kAlwaysEqual) {
+    if (std::addressof(__other) != this) {
+      if (__kPropOnMoveAssign) {
+        auto __alloc = __other.__alloc_;
+        new (this) basic_stacktrace(std::move(__other), __alloc);
+      } else {
+        new (this) basic_stacktrace(std::move(__other));
+      }
+    }
+    return *this;
+  }
+
+  // (19.6.4.3)
+  // [stacktrace.basic.obs], observers
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __alloc_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const noexcept { return __entries_.begin(); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const noexcept { return __entries_.end(); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rbegin() const noexcept { return __entries_.rbegin(); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const noexcept { return __entries_.rend(); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const noexcept { return __entries_.cbegin(); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator cend() const noexcept { return __entries_.cend(); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crbegin() const noexcept { return __entries_.crbegin(); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const noexcept { return __entries_.crend(); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool empty() const noexcept { return __entries_.empty(); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_type size() const noexcept { return __entries_.size(); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_type max_size() const noexcept { return __entries_.max_size(); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_reference operator[](size_type __i) const { return __entries_[__i]; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_reference at(size_type __i) const { return __entries_.at(__i); }
+
+  // (19.6.4.4)
+  // [stacktrace.basic.cmp], comparisons
+
+  template <class _Allocator2>
+  _LIBCPP_HIDE_FROM_ABI friend bool
+  operator==(const basic_stacktrace& __x, const basic_stacktrace<_Allocator2>& __y) noexcept {
+    if (__x.size() != __y.size()) {
+      return false;
+    }
+    auto __xi = __x.begin();
+    auto __yi = __y.begin();
+    auto __xe = __x.end();
+    while (__xi != __xe) {
+      if (*__xi++ != *__yi++) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  template <class _Allocator2>
+  _LIBCPP_HIDE_FROM_ABI friend strong_ordering
+  operator<=>(const basic_stacktrace& __x, const basic_stacktrace<_Allocator2>& __y) noexcept {
+    auto __ret = __x.size() <=> __y.size();
+    if (__ret != std::strong_ordering::equal) {
+      return __ret;
+    }
+    auto __xi = __x.begin();
+    auto __yi = __y.begin();
+    auto __xe = __x.end();
+    while ((__ret == std::strong_ordering::equal) && __xi != __xe) {
+      __ret = *__xi++ <=> *__yi++;
+    }
+    return __ret;
+  }
+
+  // (19.6.4.5)
+  // [stacktrace.basic.mod], modifiers
+
+  _LIBCPP_HIDE_FROM_ABI void swap(basic_stacktrace& __other) noexcept(
+      allocator_traits<_Allocator>::propagate_on_container_swap::value ||
+      allocator_traits<_Allocator>::is_always_equal::value) {
+    std::swap(__entries_, __other.__entries_);
+    if (__kPropOnSwap) {
+      std::swap(__alloc_, __other.__alloc_);
+    }
+  }
+};
+
+using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
+
+namespace pmr {
+using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
+} // namespace pmr
+
+// (19.6.4.6)
+// Non-member functions [stacktrace.basic.nonmem]
+
+template <class _Allocator>
+_LIBCPP_HIDE_FROM_ABI inline void
+swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b) noexcept(noexcept(__a.swap(__b))) {
+  __a.swap(__b);
+}
+
+#  if _LIBCPP_HAS_LOCALIZATION
+template <class _Allocator>
+_LIBCPP_HIDE_FROM_ABI inline ostream& operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __stacktrace) {
+  return ((__stacktrace::base const&)__stacktrace).write_to(__os);
+}
+template <class _Allocator>
+_LIBCPP_HIDE_FROM_ABI inline string to_string(const basic_stacktrace<_Allocator>& __stacktrace) {
+  return ((__stacktrace::base const&)__stacktrace).to_string();
+}
+#  endif // _LIBCPP_HAS_LOCALIZATION
+
+// (19.6.6)
+// Hash support [stacktrace.basic.hash]
+
+template <class _Allocator>
+struct hash<basic_stacktrace<_Allocator>> {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(basic_stacktrace<_Allocator> const& __context) const noexcept {
+    size_t __ret = 1;
+    for (auto const& __entry : __context.__entries_) {
+      __ret += hash<uintptr_t>()(__entry.native_handle());
+    }
+    return __ret;
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STACKTRACE_BASIC
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
new file mode 100644
index 0000000000000..48daf14ccccc6
--- /dev/null
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -0,0 +1,211 @@
+// -*- 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_STACKTRACE_ENTRY
+#define _LIBCPP_STACKTRACE_ENTRY
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#include <__assert>
+#include <__functional/function.h>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+
+#if _LIBCPP_HAS_LOCALIZATION
+#  include <__fwd/format.h>
+#  include <__fwd/ostream.h>
+#endif // _LIBCPP_HAS_LOCALIZATION
+
+#if _LIBCPP_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class stacktrace_entry;
+
+namespace __stacktrace {
+
+struct str;
+
+template <class _Tp>
+struct str_alloc {
+  using result_t _LIBCPP_NODEBUG = allocation_result<_Tp*, size_t>;
+
+  // Lambdas wrap the caller's allocator, re-bound so we can deal with `chars`.
+  function<char*(size_t)> __alloc_;
+  function<void(char*, size_t)> __dealloc_;
+
+  // This only works with chars or other 1-byte things.
+  static_assert(sizeof(_Tp) == 1);
+
+  using value_type = _Tp;
+  using pointer    = _Tp*;
+
+  template <class _Tp2>
+  struct rebind {
+    using other = str_alloc<_Tp2>;
+  };
+
+  str_alloc(const str_alloc&)            = default;
+  str_alloc(str_alloc&&)                 = default;
+  str_alloc& operator=(const str_alloc&) = default;
+  str_alloc& operator=(str_alloc&&)      = default;
+
+  str_alloc(function<char*(size_t)> __alloc, function<void(char*, size_t)> __dealloc)
+      : __alloc_(std::move(__alloc)), __dealloc_(std::move(__dealloc)) {}
+
+  template <class _A0, // some allocator; can be of any type
+            class _AT = allocator_traits<_A0>,
+            class _CA = typename _AT::template rebind_alloc<char>>
+    requires __is_allocator_v<_A0>
+  static str_alloc make(_A0 __a) {
+    auto __ca = _CA(__a);
+    return {[__ca](size_t __n) mutable -> char* { return __ca.allocate(__n); },
+            [__ca](char* __p, size_t __n) mutable { __ca.deallocate(__p, __n); }};
+  }
+
+  _Tp* allocate(size_t __n) { return __alloc_(__n); }
+  void deallocate(_Tp* __p, size_t __n) { __dealloc_(__p, __n); }
+  bool operator==(str_alloc<_Tp> const& __rhs) const { return std::addressof(__rhs) == this; }
+};
+
+struct str : basic_string<char, char_traits<char>, str_alloc<char>> {
+  using base = basic_string<char, char_traits<char>, str_alloc<char>>;
+  _LIBCPP_HIDE_FROM_ABI str(str_alloc<char> const& __alloc) : base(__alloc) {}
+  _LIBCPP_HIDE_FROM_ABI string_view view() const { return {this->data(), this->size()}; }
+};
+
+struct image;
+
+struct entry_base {
+  constexpr static size_t __max_sym_len = 512;
+#  if defined(PATH_MAX)
+  constexpr static size_t __max_file_len = PATH_MAX;
+#  elif defined(MAX_PATH)
+  constexpr static size_t __max_file_len = MAX_PATH;
+#  else
+  constexpr static size_t __max_file_len = (1 << 10);
+#  endif
+
+  uintptr_t __addr_{};
+  optional<str> __desc_{};
+  optional<str> __file_{};
+  uint_least32_t __line_{};
+  image const* __image_{};
+
+  _LIBCPP_HIDE_FROM_ABI str& assign_desc(str&& __s) { return *(__desc_ = std::move(__s)); }
+  _LIBCPP_HIDE_FROM_ABI str& assign_file(str&& __s) { return *(__file_ = std::move(__s)); }
+
+#  if _LIBCPP_HAS_LOCALIZATION
+  _LIBCPP_EXPORTED_FROM_ABI std::ostream& write_to(std::ostream& __os) const;
+  _LIBCPP_EXPORTED_FROM_ABI string to_string() const;
+#  endif // _LIBCPP_HAS_LOCALIZATION
+
+  _LIBCPP_HIDE_FROM_ABI uintptr_t adjusted_addr() const;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr static entry_base* of(auto& __s) { return static_cast<entry_base*>(__s); }
+
+  _LIBCPP_HIDE_FROM_ABI ~entry_base()                                      = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr entry_base()                             = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr entry_base(const entry_base&)            = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr entry_base& operator=(const entry_base&) = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr entry_base(entry_base&&)                 = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr entry_base& operator=(entry_base&&)      = default;
+};
+
+} // namespace __stacktrace
+
+class stacktrace_entry {
+  friend _LIBCPP_HIDE_FROM_ABI inline ostream& operator<<(ostream& __os, std::stacktrace_entry const& __entry);
+  friend _LIBCPP_HIDE_FROM_ABI inline string to_string(std::stacktrace_entry const& __entry);
+
+  __stacktrace::entry_base __base_{};
+
+public:
+  // (19.6.3.1) Overview [stacktrace.entry.overview]
+  using native_handle_type = uintptr_t;
+
+  // (19.6.3.2) [stacktrace.entry.cons], constructors
+  _LIBCPP_HIDE_FROM_ABI ~stacktrace_entry() noexcept                                            = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr stacktrace_entry() noexcept                                   = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr stacktrace_entry(const stacktrace_entry&) noexcept            = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr stacktrace_entry& operator=(const stacktrace_entry&) noexcept = default;
+
+  // (19.6.3.3) [stacktrace.entry.obs], observers
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr native_handle_type native_handle() const noexcept {
+    return __base_.__addr_;
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return native_handle() != 0; }
+
+  // (19.6.3.4) [stacktrace.entry.query], query
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string description() const {
+    return __base_.__desc_ ? string(*__base_.__desc_) : string();
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string source_file() const {
+    return __base_.__file_ ? string(*__base_.__file_) : string();
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI uint_least32_t source_line() const { return __base_.__line_; }
+
+  // (19.6.3.5) [stacktrace.entry.cmp], comparison
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+    return __x.native_handle() == __y.native_handle();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr strong_ordering
+  operator<=>(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+    return __x.native_handle() <=> __y.native_handle();
+  }
+};
+
+// (19.6.4.6)
+// Non-member functions [stacktrace.basic.nonmem]
+
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI string to_string(const stacktrace_entry& __entry);
+
+#  if _LIBCPP_HAS_LOCALIZATION
+_LIBCPP_HIDE_FROM_ABI inline ostream& operator<<(ostream& __os, std::stacktrace_entry const& __entry) {
+  return __entry.__base_.write_to(__os);
+}
+_LIBCPP_HIDE_FROM_ABI inline string to_string(std::stacktrace_entry const& __entry) {
+  return __entry.__base_.to_string();
+}
+#  endif // _LIBCPP_HAS_LOCALIZATION
+
+// (19.6.5)
+// Formatting support [stacktrace.format]:
+// https://github.com/llvm/llvm-project/issues/105257
+
+// (19.6.6)
+// Hash support [stacktrace.basic.hash]
+
+template <>
+struct _LIBCPP_HIDE_FROM_ABI hash<stacktrace_entry> {
+  [[nodiscard]] size_t operator()(stacktrace_entry const& __entry) const noexcept {
+    auto __addr = __entry.native_handle();
+    return hash<uintptr_t>()(__addr);
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STACKTRACE_ENTRY
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index a86d6c6a43d0e..034dd93f1c27c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2014,6 +2014,31 @@ module std [system] {
     export *
   }
 
+  module stacktrace {
+    module basic_stacktrace       { header "__stacktrace/basic_stacktrace.h" }
+    module stacktrace_entry       { header "__stacktrace/stacktrace_entry.h" }
+
+    header "stacktrace"
+    export *
+    // TODO: Workaround for https://github.com/llvm/llvm-project/issues/120108
+    export std.cstddef.byte
+    export std.cstddef.size_t
+    export std.format.escaped_output_table
+    export std.format.formatter
+    export std.format.width_estimation_table
+    export std.functional.function
+    export std.functional.hash
+    export std.iterator.iterator
+    export std.iterator.reverse_iterator
+    export std.memory.allocator
+    export std.memory.allocator_traits
+    export std.memory_resource.polymorphic_allocator
+    export std.new.allocate
+    export std.ostream.basic_ostream
+    export std.type_traits.is_nothrow_constructible
+    export std.vector.vector
+  }
+
   module stdexcept {
     header "stdexcept"
     export *
diff --git a/libcxx/include/stacktrace b/libcxx/include/stacktrace
new file mode 100644
index 0000000000000..106731d9be2d1
--- /dev/null
+++ b/libcxx/include/stacktrace
@@ -0,0 +1,187 @@
+// -*- 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_STACKTRACE
+#define _LIBCPP_STACKTRACE
+
+/*
+  Header <stacktrace> synopsis
+  (19.6.2)
+
+#include <compare>  // see [compare.syn]
+
+namespace std {
+  // [stacktrace.entry], class stacktrace_entry
+  class stacktrace_entry;
+
+  // [stacktrace.basic], class template basic_stacktrace
+  template<class Allocator>
+    class basic_stacktrace;
+
+  // basic_stacktrace typedef-names
+  using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
+
+  // [stacktrace.basic.nonmem], non-member functions
+  template<class Allocator>
+    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+      noexcept(noexcept(a.swap(b)));
+
+  string to_string(const stacktrace_entry& f);
+
+  template<class Allocator>
+    string to_string(const basic_stacktrace<Allocator>& st);
+
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  template<class Allocator>
+    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+
+  // [stacktrace.format], formatting support
+  // Not yet implemented; see https://github.com/llvm/llvm-project/issues/105257
+  template<> struct formatter<stacktrace_entry>;
+  template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+
+  namespace pmr {
+    using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
+  }
+
+  // [stacktrace.basic.hash], hash support
+  template<class T> struct hash;
+  template<> struct hash<stacktrace_entry>;
+  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;
+}
+
+// [stacktrace.entry]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    using native_handle_type = implementation-defined;
+
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry() noexcept;
+    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
+    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
+
+    ~stacktrace_entry();
+
+    // [stacktrace.entry.obs], observers
+    constexpr native_handle_type native_handle() const noexcept;
+    constexpr explicit operator bool() const noexcept;
+
+    // [stacktrace.entry.query], query
+    string description() const;
+    string source_file() const;
+    uint_least32_t source_line() const;
+
+    // [stacktrace.entry.cmp], comparison
+    friend constexpr bool operator==(const stacktrace_entry& x,
+                                     const stacktrace_entry& y) noexcept;
+    friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+                                                 const stacktrace_entry& y) noexcept;
+  };
+}
+
+// [stacktrace.basic]
+
+namespace std {
+  template<class Allocator>
+  class basic_stacktrace {
+  public:
+    using value_type = stacktrace_entry;
+    using const_reference = const value_type&;
+    using reference = value_type&;
+    using const_iterator = implementation-defined;  // see [stacktrace.basic.obs]
+    using iterator = const_iterator;
+    using reverse_iterator = reverse_iterator<iterator>;
+    using const_reverse_iterator = reverse_iterator<const_iterator>;
+    using difference_type = implementation-defined;
+    using size_type = implementation-defined;
+    using allocator_type = Allocator;
+
+    // [stacktrace.basic.cons], creation and assignment
+    static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+    static basic_stacktrace current(size_type skip,
+                                    const allocator_type& alloc = allocator_type()) noexcept;
+    static basic_stacktrace current(size_type skip, size_type max_depth,
+                                    const allocator_type& alloc = allocator_type()) noexcept;
+
+    basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+    explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+
+    basic_stacktrace(const basic_stacktrace& other);
+    basic_stacktrace(basic_stacktrace&& other) noexcept;
+    basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+    basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+    basic_stacktrace& operator=(const basic_stacktrace& other);
+    basic_stacktrace& operator=(basic_stacktrace&& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+        allocator_traits<Allocator>::is_always_equal::value);
+
+    ~basic_stacktrace();
+
+    // [stacktrace.basic.obs], observers
+    allocator_type get_allocator() const noexcept;
+
+    const_iterator begin() const noexcept;
+    const_iterator end() const noexcept;
+    const_reverse_iterator rbegin() const noexcept;
+    const_reverse_iterator rend() const noexcept;
+
+    const_iterator cbegin() const noexcept;
+    const_iterator cend() const noexcept;
+    const_reverse_iterator crbegin() const noexcept;
+    const_reverse_iterator crend() const noexcept;
+
+    bool empty() const noexcept;
+    size_type size() const noexcept;
+    size_type max_size() const noexcept;
+
+    const_reference operator[](size_type) const;
+    const_reference at(size_type) const;
+
+    // [stacktrace.basic.cmp], comparisons
+    template<class Allocator2>
+    friend bool operator==(const basic_stacktrace& x,
+                           const basic_stacktrace<Allocator2>& y) noexcept;
+    template<class Allocator2>
+    friend strong_ordering operator<=>(const basic_stacktrace& x,
+                                       const basic_stacktrace<Allocator2>& y) noexcept;
+
+    // [stacktrace.basic.mod], modifiers
+    void swap(basic_stacktrace& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+        allocator_traits<Allocator>::is_always_equal::value);
+
+  private:
+    vector<value_type, allocator_type> frames_;         // exposition only
+  };
+}
+
+*/
+
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+#  include <__cxx03/__config>
+#else
+#  include <__config>
+
+#  if _LIBCPP_STD_VER >= 23
+#    include <__stacktrace/basic_stacktrace.h>
+#    include <__stacktrace/stacktrace_entry.h>
+#    include <compare> // per [stacktrace.syn]
+#  endif
+
+#  include <version>
+
+#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#    pragma GCC system_header
+#  endif
+
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
+#endif // _LIBCPP_STACKTRACE
diff --git a/libcxx/include/version b/libcxx/include/version
index 0fef1bb87cf60..c4fb60e7fba54 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -532,7 +532,9 @@ __cpp_lib_void_t                                        201411L <type_traits>
 // # define __cpp_lib_ranges_zip                           202110L
 // # define __cpp_lib_reference_from_temporary             202202L
 // # define __cpp_lib_spanstream                           202106L
-// # define __cpp_lib_stacktrace                           202011L
+# if _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+#   define __cpp_lib_stacktrace                         202011L
+# endif
 # define __cpp_lib_stdatomic_h                          202011L
 # define __cpp_lib_string_contains                      202011L
 # define __cpp_lib_string_resize_and_overwrite          202110L
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 3a1d8950c2db0..0998827ebf058 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
@@ -413,6 +413,10 @@
 {'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace10entry_base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace10entry_base9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace4base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace4base9to_stringEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__112bad_weak_ptr4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm', 'type': 'FUNC'}
@@ -534,6 +538,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'}
@@ -974,6 +979,9 @@
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base12current_implEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD2Ev', 'type': 'FUNC'}
@@ -1125,6 +1133,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 +1314,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 +1516,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/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 90166073b135f..f6d454ccb2f23 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -104,6 +104,10 @@
 {'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base9to_stringEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__112bad_weak_ptr4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm', 'type': 'FUNC'}
@@ -225,6 +229,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'}
@@ -622,6 +627,9 @@
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base12current_implEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD2Ev', 'type': 'FUNC'}
@@ -773,6 +781,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'}
@@ -953,7 +962,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': 33, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__114__num_put_base12__format_intEPcPKcbj', 'type': 'FUNC'}
@@ -1156,7 +1164,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/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 5855c17cf11ed..15be31d56ab1e 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -75,6 +75,10 @@
 {'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base9to_stringEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__112bad_weak_ptr4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm', 'type': 'FUNC'}
@@ -196,6 +200,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'}
@@ -593,6 +598,9 @@
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base12current_implEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD2Ev', 'type': 'FUNC'}
@@ -744,6 +752,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'}
@@ -923,7 +932,6 @@
 {'is_defined': True, 'name': '_ZNSt3__113random_deviceclEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__113shared_futureIvED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__113shared_futureIvED2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__113__hash_memoryEPKvm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__113shared_futureIvEaSERKS1_', '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': 33, 'type': 'OBJECT'}
@@ -1127,7 +1135,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/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index dd7385bf33a42..91d4b051adac4 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -69,9 +69,6 @@ module;
 #  if __has_include(<spanstream>)
 #    error "please update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<spanstream>)
-#  if __has_include(<stacktrace>)
-#    error "please update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<stacktrace>)
 #  if __has_include(<stdfloat>)
 #    error "please update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<stdfloat>)
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index 984b18321923c..a70199d6f3b17 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -102,6 +102,7 @@ module;
 #include <span>
 #include <sstream>
 #include <stack>
+#include <stacktrace>
 #include <stdexcept>
 #include <stop_token>
 #include <streambuf>
@@ -153,9 +154,6 @@ module;
 #  if __has_include(<spanstream>)
 #    error "please update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<spanstream>)
-#  if __has_include(<stacktrace>)
-#    error "please update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<stacktrace>)
 #  if __has_include(<stdfloat>)
 #    error "please update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<stdfloat>)
diff --git a/libcxx/modules/std/stacktrace.inc b/libcxx/modules/std/stacktrace.inc
index c1184087c0df4..19040b729d9f8 100644
--- a/libcxx/modules/std/stacktrace.inc
+++ b/libcxx/modules/std/stacktrace.inc
@@ -8,7 +8,8 @@
 //===----------------------------------------------------------------------===//
 
 export namespace std {
-#if 0
+#if _LIBCPP_STD_VER >= 23
+
   // [stacktrace.entry], class stacktrace_­entry
   using std::stacktrace_entry;
 
@@ -21,9 +22,10 @@ export namespace std {
   // [stacktrace.basic.nonmem], non-member functions
   using std::swap;
 
+#  if _LIBCPP_HAS_LOCALIZATION
   using std::to_string;
-
   using std::operator<<;
+#  endif // _LIBCPP_HAS_LOCALIZATION
 
   namespace pmr {
     using std::pmr::stacktrace;
@@ -31,5 +33,11 @@ export namespace std {
 
   // [stacktrace.basic.hash], hash support
   using std::hash;
-#endif
+
+  // [stacktrace.format], formatting support
+  // (Excluded for now; issue #105257 will implement
+  // P2693R1, "Formatting thread::id and stacktrace")
+  // using std::formatter;
+
+#endif // _LIBCPP_STD_VER >= 23
 } // namespace std
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index f59fe0e08fccb..aca66727d6442 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -40,6 +40,11 @@ set(LIBCXX_SOURCES
   ryu/d2fixed.cpp
   ryu/d2s.cpp
   ryu/f2s.cpp
+  stacktrace/images.cpp
+  stacktrace/impl_generic.cpp
+  stacktrace/impl_windows.cpp
+  stacktrace/impl.cpp
+  stacktrace/unwinding.h
   stdexcept.cpp
   string.cpp
   support/runtime/exception_fallback.ipp
diff --git a/libcxx/src/stacktrace/images.cpp b/libcxx/src/stacktrace/images.cpp
new file mode 100644
index 0000000000000..4120ff409bb3c
--- /dev/null
+++ b/libcxx/src/stacktrace/images.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+//
+// OS-specific construction
+//
+
+#include "__config"
+
+#if defined(__APPLE__)
+// MacOS-specific: use the `dyld` loader to access info about loaded Mach-O images.
+#  include "stacktrace/images.h"
+#  include <algorithm>
+#  include <cstdlib>
+#  include <dlfcn.h>
+#  include <mach-o/dyld.h>
+#  include <mach-o/loader.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+images::images() {
+  images_[count_++] = {0uz, 0};  // sentinel at low end
+  images_[count_++] = {~0uz, 0}; // sentinel at high end
+  auto dyld_count   = _dyld_image_count();
+  for (unsigned i = 0; i < dyld_count && count_ < k_max_images; i++) {
+    auto& image         = images_[count_++];
+    image.slide_        = uintptr_t(_dyld_get_image_vmaddr_slide(i));
+    image.loaded_at_    = uintptr_t(_dyld_get_image_header(i));
+    image.is_main_prog_ = (i == 0);
+    strncpy(image.name_, _dyld_get_image_name(i), sizeof(image.name_));
+  }
+  std::sort(images_.begin(), images_.begin() + count_);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#elif !defined(_WIN32)
+// Non-MacOS and non-Windows, including Linux: assume environment has these headers.
+#  include "stacktrace/images.h"
+#  include <algorithm>
+#  include <cstdlib>
+#  include <dlfcn.h>
+#  include <link.h>
+#  include <unistd.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+int add_image(dl_phdr_info* info, size_t, void* images_v) {
+  auto& imgs = *(images*)images_v;
+  if (imgs.count_ == images::k_max_images) {
+    return 0;
+  }
+  auto is_first = (imgs.count_ == 0);
+  auto& image   = imgs.images_.at(imgs.count_++);
+  // Absolute address at which this ELF is loaded
+  image.loaded_at_ = info->dlpi_addr;
+  // This also happens to be the "slide" amount since ELF has zero-relative offsets
+  image.slide_ = info->dlpi_addr;
+  strncpy(image.name_, info->dlpi_name, sizeof(image.name_));
+  // `dl_iterate_phdr` gives us the main program image first
+  image.is_main_prog_ = is_first;
+  if (!image.name_[0] && is_first) {
+    char buf[entry_base::__max_file_len]{0};
+    if (readlink("/proc/self/exe", buf, sizeof(buf)) != -1) { // Ignores errno if error
+      strncpy(image.name_, buf, sizeof(image.name_));
+    }
+  }
+  // If we're at the limit, return nonzero to stop iterating
+  return imgs.count_ == images::k_max_images;
+}
+} // namespace
+
+images::images() {
+  dl_iterate_phdr(add_image, this);
+  images_[count_++] = {0uz, 0};  // sentinel at low end
+  images_[count_++] = {~0uz, 0}; // sentinel at high end
+  std::sort(images_.begin(), images_.begin() + count_);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/stacktrace/images.h b/libcxx/src/stacktrace/images.h
new file mode 100644
index 0000000000000..5c7af94de31d6
--- /dev/null
+++ b/libcxx/src/stacktrace/images.h
@@ -0,0 +1,119 @@
+// -*- 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_STACKTRACE_IMAGES_H
+#define _LIBCPP_STACKTRACE_IMAGES_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 23
+
+#  include <__stacktrace/stacktrace_entry.h>
+#  include <array>
+#  include <cstdint>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct image;
+struct images;
+
+struct image {
+  uintptr_t loaded_at_{};
+  uintptr_t slide_{};
+  char name_[__stacktrace::entry_base::__max_file_len]{0};
+  bool is_main_prog_{};
+
+  _LIBCPP_HIDE_FROM_ABI bool operator<(image const& __rhs) const {
+    if (loaded_at_ < __rhs.loaded_at_) {
+      return true;
+    }
+    if (loaded_at_ > __rhs.loaded_at_) {
+      return false;
+    }
+    return strcmp(name_, __rhs.name_) < 0;
+  }
+  _LIBCPP_HIDE_FROM_ABI operator bool() const { return name_[0]; }
+};
+
+/**
+ * Contains an array `images_`, which will include `prog_image` objects (see above)
+ * collected in an OS-dependent way.  After construction these images will be sorted
+ * according to their load address; there will also be two sentinels with dummy
+ * addresses (0x0000... and 0xFFFF...) to simplify search functions.
+ *
+ * After construction, images_ and count_ look like:
+ *  [0]             [1]             [2]             [3]       ...     [count_ - 1]
+ *  (sentinel)      foo.exe         libc++so.1      libc.so.6         (sentinel)
+ *  0x000000000000  0x000100000000  0x633b00000000  0x7c5500000000    0xffffffffffff
+ */
+struct images {
+  constexpr static size_t k_max_images = 256;
+  std::array<image, k_max_images + 2> images_{}; // space for the L/R sentinels
+  unsigned count_{0};                            // image count, including sentinels
+
+  /** An OS-specific constructor is defined. */
+  _LIBCPP_EXPORTED_FROM_ABI images();
+
+  /** Get prog_image by index (0 <= index < count_) */
+  _LIBCPP_HIDE_FROM_ABI image& operator[](size_t __index) {
+    _LIBCPP_ASSERT(__index < count_, "index out of range");
+    return images_.at(__index);
+  }
+
+  /** Image representing the main program, or nullptr if we couldn't find it */
+  _LIBCPP_HIDE_FROM_ABI image* main_prog_image() {
+    for (size_t __i = 1; __i < count_ - 1; __i++) {
+      auto& __image = images_[__i];
+      if (__image.is_main_prog_) {
+        return &__image;
+      }
+    }
+    return nullptr;
+  }
+
+  /** Search the sorted images array for one containing this address. */
+  _LIBCPP_HIDE_FROM_ABI void find(size_t* __index, uintptr_t __addr) {
+    // `index` slides left/right as we search through images.
+    // It's (probably) likely several consecutive entries are from the same image, so
+    // each iteration's `find` uses the same starting point, making it (probably) constant-time.
+    // XXX Is this more efficient in practice than e.g. `std::set` and `upper_bound`?
+    if (*__index < 1) {
+      *__index = 1;
+    }
+    if (*__index > count_ - 1) {
+      *__index = count_ - 1;
+    }
+    while (images_[*__index]) {
+      if (__addr < images_[*__index].loaded_at_) {
+        --*__index;
+      } else if (__addr >= images_[*__index + 1].loaded_at_) {
+        ++*__index;
+      } else {
+        break;
+      }
+    }
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STACKTRACE_IMAGES_H
diff --git a/libcxx/src/stacktrace/impl.cpp b/libcxx/src/stacktrace/impl.cpp
new file mode 100644
index 0000000000000..c3b9f906a4cf3
--- /dev/null
+++ b/libcxx/src/stacktrace/impl.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
+#include <string>
+
+#if _LIBCPP_HAS_LOCALIZATION
+#  include <iomanip>
+#  include <iostream>
+#  include <sstream>
+#endif //_LIBCPP_HAS_LOCALIZATION
+
+#include "stacktrace/images.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __stacktrace {
+
+#if _LIBCPP_HAS_LOCALIZATION
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& entry_base::write_to(ostream& __os) const {
+  // Although 64-bit addresses are 16 nibbles long, they're often <= 0x7fff_ffff_ffff
+  constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
+
+  __os << "0x" << std::hex << std::setfill('0') << std::setw(__k_addr_width) << __addr_;
+  if (__desc_) {
+    __os << ": " << __desc_->view();
+  }
+  if (__file_) {
+    __os << ": " << __file_->view();
+  }
+  if (__line_) {
+    __os << ":" << std::dec << __line_;
+  }
+  return __os;
+}
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& base::write_to(std::ostream& __os) const {
+  auto iters = __entry_iters_();
+  auto count = iters.size();
+  if (!count) {
+    __os << "(empty stacktrace)";
+  } else {
+    for (size_t __i = 0; __i < count; __i++) {
+      // Insert newlines between entries (but not before the first or after the last)
+      if (__i) {
+        __os << '\n';
+      }
+      __os << "  frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": "
+           << *(stacktrace_entry const*)(iters.data() + __i);
+    }
+  }
+  return __os;
+}
+
+_LIBCPP_EXPORTED_FROM_ABI string entry_base::to_string() const {
+  stringstream __ss;
+  write_to(__ss);
+  return __ss.str();
+}
+
+_LIBCPP_EXPORTED_FROM_ABI string base::to_string() const {
+  stringstream __ss;
+  write_to(__ss);
+  return __ss.str();
+}
+
+#endif // _LIBCPP_HAS_LOCALIZATION
+
+_LIBCPP_HIDE_FROM_ABI uintptr_t entry_base::adjusted_addr() const {
+  auto sub = __image_ ? __image_->slide_ : 0;
+  return __addr_ - sub;
+}
+
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/impl_generic.cpp b/libcxx/src/stacktrace/impl_generic.cpp
new file mode 100644
index 0000000000000..315e0f21c30aa
--- /dev/null
+++ b/libcxx/src/stacktrace/impl_generic.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+/*
+"Generic" implementation for any platform that doesn't have its own special implementation.
+(Currently this means any platform other than Windows)
+*/
+
+#if !defined(_WIN32)
+
+#  include <__config>
+#  include <__stacktrace/stacktrace_entry.h>
+
+#  include "stacktrace/images.h"
+#  include "stacktrace/unwinding.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
+base::current_impl(size_t skip, size_t max_depth) {
+  if (!max_depth) [[unlikely]] {
+    return;
+  }
+
+  /*
+  Build up the stacktrace entries and fill out their fields the best we can.
+  An `entry_base` structure looks more or less like:
+
+    __addr_     uintptr_t   Instruction address (of a call instruction, faulting insn, ...)
+    __desc_     string      "Description" which we'll say is synonymous with "symbol"
+    __file_     string      Source filename, or if that's not available, program/library path
+    __line_     int         Line number within the source file, or failing that, zero
+    __image_    image*      The image, loaded in by the OS, containing that address (program, library)
+
+  On Windows, there are DLLs which take care of all this (dbghelp, psapi), with essentially
+  zero overlap with any other OS, so it's in its own file (impl_windows).  Otherwise, i.e.
+  on non-Windows platforms, taking a stacktrace looks like:
+
+  0. Create the basic_stacktrace, its internal vector, using provided allocator.
+     (This was handled by the `current` member functions inside `basic_stacktrace`.)
+  1. Collect instruction addresses to build up the entries, observing skip and max_depth.
+     The unwinding library we have available to us should take care of walking the call stack
+     and finding addresses.
+  2. Resolve addresses into the program images (executable, libraries); normalize the addresses
+     from step 1, as they were seen in the wild, into program-image-relative addresses
+     (i.e. deal with ASLR)
+  3. Resolve adjusted addresses into their symbols; some environments provide this out of the box
+     (MacOS) and others make this less easy (Linux)
+  4. To get the source file and line number, we have to dig through debug information (DWARF format);
+     we might need the help of a library.
+  */
+
+  unwind_addrs(*this, skip + 1, max_depth);
+  if (__entry_iters_().size()) {
+    find_images();
+    find_symbols();
+    find_source_locs();
+  }
+}
+
+void base::find_images() {
+  images images;
+  size_t i = 0;
+  for (auto& entry : __entry_iters_()) {
+    images.find(&i, entry.__addr_);
+    if (auto& image = images[i]) {
+      entry.__image_ = ℑ
+      // While we're in this loop, get the executable's path, and tentatively use this for source file.
+      entry.assign_file(__create_str()).assign(image.name_);
+    }
+  }
+}
+
+void base::find_symbols() {
+  // TODO
+}
+
+void base::find_source_locs() {
+  // TODO
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/stacktrace/impl_windows.cpp b/libcxx/src/stacktrace/impl_windows.cpp
new file mode 100644
index 0000000000000..c739f3731c175
--- /dev/null
+++ b/libcxx/src/stacktrace/impl_windows.cpp
@@ -0,0 +1,248 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(_WIN32)
+
+#  define WIN32_LEAN_AND_MEAN
+#  include <windows.h>
+//
+#  include <dbghelp.h>
+#  include <psapi.h>
+//
+#  include <cstring>
+#  include <iostream>
+#  include <mutex>
+#  include <stacktrace>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+struct dll {
+  HMODULE module_{};
+  bool loaded_{};
+
+  explicit dll(char const* name) : module_(LoadLibraryA(name)) {}
+
+  ~dll() {
+    if (module_) {
+      FreeLibrary(module_);
+    }
+  }
+
+  template <typename F>
+  bool get_func(F** func, char const* name) {
+    return ((*func = (F*)(void*)GetProcAddress(module_, name)) != nullptr);
+  }
+};
+
+// clang-format off
+
+struct dbghelp_dll final : dll {
+  IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
+  bool    (WINAPI *SymCleanup)         (HANDLE);
+  DWORD   (WINAPI *SymGetOptions)      ();
+  bool    (WINAPI *SymGetSearchPath)   (HANDLE, char const*, DWORD);
+  bool    (WINAPI *SymInitialize)      (HANDLE, char const*, bool);
+  DWORD   (WINAPI *SymSetOptions)      (DWORD);
+  bool    (WINAPI *SymSetSearchPath)   (HANDLE, char const*);
+  bool    (WINAPI *StackWalk64)        (DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, void*, void*, void*, void*);
+  void*   (WINAPI *SymFunctionTableAccess64)(HANDLE, DWORD64);
+  bool    (WINAPI *SymGetLineFromAddr64)(HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*);
+  DWORD64 (WINAPI *SymGetModuleBase64) (HANDLE, DWORD64);
+  bool    (WINAPI *SymGetModuleInfo64) (HANDLE, DWORD64, IMAGEHLP_MODULE64*);
+  bool    (WINAPI *SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
+  DWORD64 (WINAPI *SymLoadModule64)    (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
+
+  dbghelp_dll() : dll("dbghelp.dll") {
+    loaded_ = true
+      && get_func(&ImageNtHeader, "ImageNtHeader")
+      && get_func(&SymCleanup, "SymCleanup")
+      && get_func(&SymGetOptions, "SymGetOptions")
+      && get_func(&SymGetSearchPath, "SymGetSearchPath")
+      && get_func(&SymInitialize, "SymInitialize")
+      && get_func(&SymSetOptions, "SymSetOptions")
+      && get_func(&SymSetSearchPath, "SymSetSearchPath")
+      && get_func(&StackWalk64, "StackWalk64")
+      && get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")
+      && get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")
+      && get_func(&SymGetModuleBase64, "SymGetModuleBase64")
+      && get_func(&SymGetModuleInfo64, "SymGetModuleInfo64")
+      && get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")
+      && get_func(&SymLoadModule64, "SymLoadModule64")
+      ;
+  }
+};
+
+struct psapi_dll final : dll {
+  bool  (WINAPI *EnumProcessModules)   (HANDLE, HMODULE*, DWORD, DWORD*);
+  bool  (WINAPI *GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
+  DWORD (WINAPI *GetModuleBaseName)    (HANDLE, HMODULE, char**, DWORD);
+
+  psapi_dll() : dll("psapi.dll") {
+    loaded_ = true
+      && get_func(&EnumProcessModules, "EnumProcessModules")
+      && get_func(&GetModuleInformation, "GetModuleInformation")
+      && get_func(&GetModuleBaseName, "GetModuleBaseNameA")
+    ;
+  }
+};
+
+struct sym_init_scope {
+  dbghelp_dll& dbghelp_;
+  HANDLE proc_;
+
+  sym_init_scope(dbghelp_dll& dbghelp, HANDLE proc)
+    : dbghelp_(dbghelp), proc_(proc) {
+    (*dbghelp_.SymInitialize)(proc_, nullptr, true);
+  }
+  ~sym_init_scope() {
+    (*dbghelp_.SymCleanup)(proc_);
+  }
+};
+
+}  // namespace
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
+base::current_impl(size_t skip, size_t max_depth) {
+  if (!max_depth) [[unlikely]] {
+    return;
+  }
+
+  static psapi_dll psapi;
+  static dbghelp_dll dbghelp;
+  if (!psapi.loaded_ || !dbghelp.loaded_) { return; }
+
+  // Not thread-safe according to docs
+  static std::mutex api_mutex;
+  std::lock_guard<std::mutex> api_guard(api_mutex);
+
+  HANDLE proc = GetCurrentProcess();
+  HMODULE exe = GetModuleHandle(nullptr);
+  if (!exe) { return; }
+
+  sym_init_scope symscope(dbghelp, proc);
+
+  char sym_path[MAX_PATH * 4]; // arbitrary
+  if (!(*dbghelp.SymGetSearchPath)(proc, sym_path, sizeof(sym_path))) { return; }
+
+  char exe_dir[MAX_PATH];
+  if (!GetModuleFileNameA(nullptr, exe_dir, sizeof(exe_dir))) { return; }
+  size_t exe_dir_len = strlen(exe_dir);
+  while (exe_dir_len > 0 && exe_dir[exe_dir_len - 1] != '\\') { exe_dir[--exe_dir_len] = 0; }
+  if (exe_dir_len > 0) { exe_dir[--exe_dir_len] = 0; }  // strip last backslash
+
+  if (!strstr(sym_path, exe_dir)) {
+    (void) strncat(sym_path, ";", sizeof(sym_path) - 1);
+    (void) strncat(sym_path, exe_dir, sizeof(sym_path) - 1);
+    if (!(*dbghelp.SymSetSearchPath)(proc, sym_path)) { return; }
+  }
+
+  IMAGE_NT_HEADERS* nt_headers;
+  if (!(nt_headers = (*dbghelp.ImageNtHeader)(exe))) { return; }
+
+  (*dbghelp.SymSetOptions)(
+    (*dbghelp.SymGetOptions)()
+    | SYMOPT_LOAD_LINES
+    | SYMOPT_UNDNAME);
+
+  auto thread  = GetCurrentThread();
+  auto machine = nt_headers->FileHeader.Machine;
+
+  CONTEXT ccx;
+  RtlCaptureContext(&ccx);
+
+  STACKFRAME64 frame;
+  memset(&frame, 0, sizeof(frame));
+  frame.AddrPC.Mode      = AddrModeFlat;
+  frame.AddrStack.Mode   = AddrModeFlat;
+  frame.AddrFrame.Mode   = AddrModeFlat;
+#if defined(_M_AMD64)
+  frame.AddrPC.Offset    = ccx.Rip;
+  frame.AddrStack.Offset = ccx.Rsp;
+  frame.AddrFrame.Offset = ccx.Rbp;
+#elif defined(_M_ARM64)
+  frame.AddrPC.Offset    = ccx.Pc;
+  frame.AddrStack.Offset = ccx.Sp;
+  frame.AddrFrame.Offset = ccx.Fp;
+#else
+# warning stacktrace requires x86-64 or ARM64; returned stacktraces will be empty
+  return;
+#endif
+
+  // Skip call to this `current_impl` func
+  ++skip;
+  
+  while (max_depth) {
+
+    if (!(*dbghelp.StackWalk64)(
+          machine, proc, thread, &frame, &ccx, nullptr,
+          (void*) dbghelp.SymFunctionTableAccess64,
+          (void*) dbghelp.SymGetModuleBase64,
+          nullptr)) {
+      break;
+    }
+
+    if (skip && skip--) { continue; }
+    if (!frame.AddrPC.Offset) { break; }
+
+    auto& entry = this->__entry_append_();
+
+    // Note: can't differentiate between a signal / exception, or a normal function call.
+    // This assumes the more common (presumably) case of normal function calls, so we'll
+    // always back up 1 byte to get into the previous (calling) instruction.
+    entry.__addr_ = frame.AddrPC.Offset - 1;
+
+    // Get the filename of the module containing this calling instruction, i.e. the program
+    // itself or a DLL.  This is used in place of the source filename, if the source filename
+    // cannot be found (missing PDB, etc.).  If the source file can be determined this will
+    // be overwritten.
+    IMAGEHLP_MODULE64 mod_info;
+    memset(&mod_info, 0, sizeof(mod_info));
+    mod_info.SizeOfStruct = sizeof(mod_info);
+    if ((*dbghelp.SymGetModuleInfo64)(proc, frame.AddrPC.Offset, &mod_info)) {
+      entry.assign_file(__create_str()).assign(mod_info.LoadedImageName);
+    }
+
+    --max_depth;
+  }
+
+  DWORD need_bytes = 0;
+  HMODULE module_handles[1024] {0};
+  if (!(*psapi.EnumProcessModules)(
+          proc, module_handles, sizeof(module_handles), LPDWORD(&need_bytes))) {
+    return;
+  }
+
+  // Symbols longer than this will be truncated.
+  static constexpr size_t kMaxSymName = 256;
+
+  for (auto& entry : __entry_iters_()) {
+    char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName + 1];
+    auto* sym          = (IMAGEHLP_SYMBOL64*)space;
+    sym->SizeOfStruct  = sizeof(IMAGEHLP_SYMBOL64);
+    sym->MaxNameLength = kMaxSymName;
+    uint64_t symdisp{0};
+    DWORD linedisp{0};
+    IMAGEHLP_LINE64 line;
+    if ((*dbghelp.SymGetSymFromAddr64)(proc, entry.__addr_, &symdisp, sym)) {
+      entry.assign_desc(__create_str()).assign(sym->Name);
+    }
+    line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+    if ((*dbghelp.SymGetLineFromAddr64)(proc, entry.__addr_, &linedisp, &line)) {
+      entry.assign_file(__create_str()).assign(line.FileName);
+      entry.__line_ = line.LineNumber;
+    }
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif  // _WIN32
diff --git a/libcxx/src/stacktrace/unwinding.h b/libcxx/src/stacktrace/unwinding.h
new file mode 100644
index 0000000000000..d363ea1bc5a15
--- /dev/null
+++ b/libcxx/src/stacktrace/unwinding.h
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_UNWIND_ADDRS_H
+#define __LIBCPP_STACKTRACE_UNWIND_ADDRS_H
+
+#include <__stacktrace/basic_stacktrace.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+void unwind_addrs(std::__stacktrace::base& base, size_t skip, size_t depth);
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#ifndef _WIN32
+
+#  if __has_include(<libunwind.h>)
+#    include <libunwind.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE inline void unwind_addrs(base& base, size_t skip, size_t depth) {
+  if (!depth) {
+    return;
+  }
+  unw_context_t cx;
+  unw_getcontext(&cx);
+  unw_cursor_t cur;
+  unw_init_local(&cur, &cx);
+  while (unw_step(&cur) > 0) {
+    if (skip && skip--) {
+      continue;
+    }
+    if (!depth--) {
+      break;
+    }
+    auto& entry = base.__entry_append_();
+    auto& eb    = (__stacktrace::entry_base&)entry;
+    unw_get_reg(&cur, UNW_REG_IP, &eb.__addr_);
+    if (!unw_is_signal_frame(&cur)) {
+      --eb.__addr_;
+    }
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#  elif __has_include(<unwind.h>)
+#    include <unwind.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct unwind_backtrace {
+  base& base_;
+  size_t skip_;
+  size_t maxDepth_;
+
+  _Unwind_Reason_Code callback(_Unwind_Context* ucx) {
+    if (skip_) {
+      --skip_;
+      return _Unwind_Reason_Code::_URC_NO_REASON;
+    }
+    if (!maxDepth_) {
+      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+    }
+    --maxDepth_;
+    int ipBefore{0};
+    auto ip = _Unwind_GetIPInfo(ucx, &ipBefore);
+    if (!ip) {
+      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+    }
+    auto& entry = base_.__entry_append_();
+    auto& eb    = (entry_base&)entry;
+    eb.__addr_  = (ipBefore ? ip : ip - 1);
+    return _Unwind_Reason_Code::_URC_NO_REASON;
+  }
+
+  static _Unwind_Reason_Code callback(_Unwind_Context* cx, void* self) {
+    return ((unwind_backtrace*)self)->callback(cx);
+  }
+};
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE inline void unwind_addrs(base& base, size_t skip, size_t depth) {
+  if (!depth) {
+    return;
+  }
+  unwind_backtrace bt{base, skip + 1, depth}; // skip this call as well
+  _Unwind_Backtrace(unwind_backtrace::callback, &bt);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#  else
+#    error need <libunwind.h> or <unwind.h>
+#  endif
+
+#endif // _WIN32
+
+#endif // __LIBCPP_STACKTRACE_UNWIND_ADDRS_H
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index cb23c7a98de1b..e33d1f07e3fc3 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -926,6 +926,27 @@ stack limits
 stack stdexcept
 stack tuple
 stack version
+stacktrace cctype
+stacktrace climits
+stacktrace compare
+stacktrace cstddef
+stacktrace cstdint
+stacktrace cstdio
+stacktrace cstring
+stacktrace cwchar
+stacktrace cwctype
+stacktrace initializer_list
+stacktrace iosfwd
+stacktrace limits
+stacktrace memory
+stacktrace optional
+stacktrace stdexcept
+stacktrace string
+stacktrace string_view
+stacktrace tuple
+stacktrace typeinfo
+stacktrace utility
+stacktrace version
 stop_token atomic
 stop_token climits
 stop_token cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index d047b29b63cc6..c89c6d50b3a4f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -908,6 +908,27 @@ stack limits
 stack stdexcept
 stack tuple
 stack version
+stacktrace cctype
+stacktrace climits
+stacktrace compare
+stacktrace cstddef
+stacktrace cstdint
+stacktrace cstdio
+stacktrace cstring
+stacktrace cwchar
+stacktrace cwctype
+stacktrace initializer_list
+stacktrace iosfwd
+stacktrace limits
+stacktrace memory
+stacktrace optional
+stacktrace stdexcept
+stacktrace string
+stacktrace string_view
+stacktrace tuple
+stacktrace typeinfo
+stacktrace utility
+stacktrace version
 stop_token atomic
 stop_token climits
 stop_token cstdint
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
new file mode 100644
index 0000000000000..f388f4fe62425
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+// (19.6.4.4) Comparisons [stacktrace.basic.cmp]
+// template<class Allocator2>
+// friend bool operator==(const basic_stacktrace& x,
+//                         const basic_stacktrace<Allocator2>& y) noexcept;
+
+#include <cassert>
+#include <stacktrace>
+
+#include "test_macros.h"
+
+// Disable TCO for calls into, and out from, the annotated function.
+#define STACKTRACE_AVOID_OPT TEST_NO_TAIL_CALLS_IN TEST_NO_TAIL_CALLS_OUT TEST_NOINLINE
+
+// Some non-inlinable functions to help contrive different stacktraces:
+// main calls the "middle" funcs, and those both call "top".
+// We'll consider main the "bottom" func, even though there are other functions
+// like `_start` which call main; those are trimmed via `max_depth` argument.
+
+STACKTRACE_AVOID_OPT std::stacktrace top(size_t skip, size_t depth) { return std::stacktrace::current(skip, depth); }
+STACKTRACE_AVOID_OPT std::stacktrace middle1(size_t skip, size_t depth) { return top(skip, depth); }
+STACKTRACE_AVOID_OPT std::stacktrace middle2(size_t skip, size_t depth) { return top(skip, depth); }
+
+STACKTRACE_AVOID_OPT int main(int, char**) {
+  // Collect a few different stacktraces and test `operator==` and `operator!=`.
+
+  std::stacktrace st0;                 // default-initializable empty stacktrace
+  static_assert(noexcept(st0 == st0)); // verify noexcept-ness
+  static_assert(noexcept(st0 != st0)); // verify noexcept-ness
+  assert(st0 == st0);                  // trivial: self-equality
+
+  std::stacktrace st1a = top(0, 2);     // st1a = [top, main]
+  assert(st1a == st1a);                 // trivial: self-equality
+  assert(st1a != st0);                  //
+  std::stacktrace st2a = middle1(0, 3); // st2a = [top, middle1, main]
+  assert(st2a == st2a);                 //
+  assert(st1a != st2a);                 //
+  std::stacktrace st2b = middle2(0, 3); // st2b = [top, middle2, main]
+  assert(st2b == st2b);                 //
+  assert(st2a != st2b);                 //
+
+  // Verify two equivalent stacktrace instances are equal, even if not "same".
+  // For both, we'll take only two entries, which should be equivalent.
+  std::stacktrace st3a = middle1(0, 2); // st3a = [top, middle1]
+  std::stacktrace st3b = middle1(0, 2); // st3b = [top, middle1]
+  assert(st3a == st3b);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp
new file mode 100644
index 0000000000000..6c03bf804b4bf
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+// (19.6.4.4) Comparisons [stacktrace.basic.cmp]
+// template<class Allocator2>
+// friend strong_ordering operator<=>(const basic_stacktrace& x,
+//                                    const basic_stacktrace<Allocator2>& y) noexcept;
+//
+// Returns: x.size() <=> y.size() if x.size() != y.size();
+// lexicographical_compare_three_way(x.begin(), x.end(), y.begin(), y.end()) otherwise.
+
+#include <cassert>
+#include <stacktrace>
+
+#include "test_macros.h"
+
+// Disable TCO for calls into, and out from, the annotated function.
+#define STACKTRACE_AVOID_OPT TEST_NO_TAIL_CALLS_IN TEST_NO_TAIL_CALLS_OUT TEST_NOINLINE
+
+// Some non-inlinable functions to help contrive different stacktraces:
+// main calls the "middle" funcs, and those both call "top".
+// We'll consider main the "bottom" func, even though there are other functions
+// like `_start` which call main; those are trimmed via `max_depth` argument.
+
+STACKTRACE_AVOID_OPT std::stacktrace top() { return std::stacktrace::current(); }
+STACKTRACE_AVOID_OPT std::stacktrace middle1() { return top(); }
+STACKTRACE_AVOID_OPT std::stacktrace middle2() { return top(); }
+
+STACKTRACE_AVOID_OPT int main(int, char**) {
+  // Collect a few different stacktraces and test `operator<=>`.
+
+  auto st1a = top(); // [top, main, ...]
+  auto st1b = st1a;
+
+  static_assert(noexcept(st1a <=> st1b));
+
+  assert(st1a == st1b);
+  auto st2a = middle1(); // [top, middle1, main, ...]
+  assert(st1a != st2a);
+  std::stacktrace empty; // []
+  auto st2b = middle2(); // [top, middle2, main, ...]
+  assert(st2a != st2b);
+
+  // empty:  []
+  // st1a:   [top, main, ...]
+  // st1b:   [top, main, ...] (copy of st1a)
+  // st2a:   [top, middle1, main:X, ...]
+  // st2b:   [top, middle2, main:Y, ...], Y > X
+
+  assert(std::strong_ordering::equal == empty <=> empty);
+  assert(std::strong_ordering::less == empty <=> st1a);
+  assert(std::strong_ordering::less == empty <=> st1b);
+  assert(std::strong_ordering::less == empty <=> st2a);
+  assert(std::strong_ordering::less == empty <=> st2b);
+
+  assert(std::strong_ordering::greater == st1a <=> empty);
+  assert(std::strong_ordering::equal == st1a <=> st1a);
+  assert(std::strong_ordering::equal == st1a <=> st1b);
+  assert(std::strong_ordering::less == st1a <=> st2a);
+  assert(std::strong_ordering::less == st1a <=> st2b);
+
+  assert(std::strong_ordering::greater == st1b <=> empty);
+  assert(std::strong_ordering::equal == st1b <=> st1a);
+  assert(std::strong_ordering::equal == st1b <=> st1b);
+  assert(std::strong_ordering::less == st1b <=> st2a);
+  assert(std::strong_ordering::less == st1b <=> st2b);
+
+  assert(std::strong_ordering::greater == st2a <=> empty);
+  assert(std::strong_ordering::greater == st2a <=> st1a);
+  assert(std::strong_ordering::greater == st2a <=> st1b);
+  assert(std::strong_ordering::equal == st2a <=> st2a);
+  assert(std::strong_ordering::less == st2a <=> st2b);
+
+  assert(std::strong_ordering::greater == st2b <=> empty);
+  assert(std::strong_ordering::greater == st2b <=> st1a);
+  assert(std::strong_ordering::greater == st2b <=> st1b);
+  assert(std::strong_ordering::greater == st2b <=> st2a);
+  assert(std::strong_ordering::equal == st2b <=> st2b);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp
new file mode 100644
index 0000000000000..50866ff5902e3
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23, has-unix-headers, libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+// XFAIL: availability-stacktrace-missing
+
+// Hardened requirements for the `current` call with given `skip` and `max_depth` amounts:
+// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3697r0.html#basic_stacktrace
+// Specifically: "Hardened preconditions: skip <= skip + max_depth is true."
+//
+// (19.6.4.2): [stacktrace.basic.cons], creation and assignment
+// static basic_stacktrace current(size_type skip, size_type max_depth,
+//                                 const allocator_type& alloc = allocator_type()) noexcept;
+
+#include <stacktrace>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::stacktrace::current(1, 0xffffffffffffffff), "sum of skip and max_depth overflows size_type");
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
new file mode 100644
index 0000000000000..2b3baffefeb50
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+// (19.6.4.2): [stacktrace.basic.cons], creation and assignment
+// basic_stacktrace(const basic_stacktrace& other);
+// basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+// basic_stacktrace& operator=(const basic_stacktrace& other);
+
+#include <cassert>
+#include <stacktrace>
+
+#include "../test_allocs.h"
+
+void test_copy_construct() {
+  std::stacktrace a = std::stacktrace::current();
+  std::stacktrace b{a};
+  assert(a == b);
+}
+
+void test_copy_assign() {
+  {
+    using A =
+        TestAlloc<std::stacktrace_entry,
+                  /*_KNoExCtors=*/true,
+                  /*_KNoExAlloc=*/true,
+                  /*_KPropagate=*/false,
+                  /*_KAlwaysEqual=*/false>;
+    std::basic_stacktrace<A> s1 = std::basic_stacktrace<A>::current();
+    std::basic_stacktrace<A> s2{s1};
+    assert(s1 == s2);
+    A a1 = s1.get_allocator();
+    A a2 = s2.get_allocator();
+    // Allocator should not propagate
+    assert(a1 != a2);
+  }
+  {
+    using A =
+        TestAlloc<std::stacktrace_entry,
+                  /*_KNoExCtors=*/true,
+                  /*_KNoExAlloc=*/true,
+                  /*_KPropagate=*/true,
+                  /*_KAlwaysEqual=*/false>;
+    std::basic_stacktrace<A> s1 = std::basic_stacktrace<A>::current();
+    std::basic_stacktrace<A> s2{s1};
+    assert(s1 == s2);
+    A a1 = s1.get_allocator();
+    A a2 = s2.get_allocator();
+    // Allocator should propagate
+    assert(a1 == a2);
+  }
+  {
+    using A =
+        TestAlloc<std::stacktrace_entry,
+                  /*_KNoExCtors=*/true,
+                  /*_KNoExAlloc=*/true,
+                  /*_KPropagate=*/false,
+                  /*_KAlwaysEqual=*/true>;
+    std::basic_stacktrace<A> s1 = std::basic_stacktrace<A>::current();
+    std::basic_stacktrace<A> s2{s1};
+    assert(s1 == s2);
+    A a1 = s1.get_allocator();
+    A a2 = s2.get_allocator();
+    // Allocator should propagate
+    assert(a1 == a2);
+  }
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  test_copy_construct();
+  test_copy_assign();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
new file mode 100644
index 0000000000000..131e6ca2ad29b
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+// (19.6.4.2): [stacktrace.basic.cons], creation and assignment
+// basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+#include "../test_allocs.h"
+
+void test_default_construct() {
+  std::stacktrace st;
+  assert(st.empty()); // Postconditions: empty() is true.
+
+  using A1 = std::allocator<std::stacktrace_entry>;
+  assert(std::basic_stacktrace<A1>().empty());
+}
+
+void test_default_construct_noexcept() {
+  static_assert(noexcept(std::stacktrace()));
+
+  using A1 = std::allocator<std::stacktrace_entry>;
+  static_assert(std::is_nothrow_default_constructible_v<A1>); // std::allocator is noexcept constructible
+  static_assert(noexcept(std::basic_stacktrace<A1>()));       // therefore this ctor should also be noexcept
+
+  using A2 =
+      TestAlloc<std::stacktrace_entry,
+                /*_KNoExCtors=*/false,
+                // Don't care about the following:
+                /*_KNoExAlloc=*/true,
+                /*_KPropagate=*/true,
+                /*_KAlwaysEqual=*/true>;
+  static_assert(!std::is_nothrow_default_constructible_v<A2>); // A2 is not noexcept constructible
+  static_assert(!noexcept(std::basic_stacktrace<A2>()));       // this ctor should not be noexcept
+}
+
+int main(int, char**) {
+  test_default_construct();
+  test_default_construct_noexcept();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
new file mode 100644
index 0000000000000..f28def7e70b6e
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+#include "../test_allocs.h"
+
+void test_construct_with_alloc() {
+  std::stacktrace st;
+  assert(st.empty());
+}
+
+void test_construct_with_alloc_noexcept() {
+  static_assert(noexcept(std::stacktrace()));
+
+  using A1 = std::allocator<std::stacktrace_entry>;
+  static_assert(std::is_nothrow_constructible_v<A1>);
+  using A2 = TestAlloc<std::stacktrace_entry, false, true, true, true>;
+  static_assert(!std::is_nothrow_constructible_v<A2>);
+
+  static_assert(!noexcept(std::basic_stacktrace<A2>(A2())));
+}
+
+int main(int, char**) {
+  test_construct_with_alloc();
+  test_construct_with_alloc_noexcept();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
new file mode 100644
index 0000000000000..bee38f57ffad0
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+*/
+
+#include <cassert>
+#include <cstdint>
+#include <stacktrace>
+
+uint32_t test1_line;
+uint32_t test2_line;
+uint32_t main_line;
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() {
+  test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret   = std::stacktrace::current();
+  return ret;
+}
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() {
+  test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret   = test1();
+  return ret;
+}
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current() {
+  main_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto st   = test2();
+
+  assert(st.size() >= 3);
+  assert(st[0]);
+  assert(st[0].native_handle());
+  // assert(st[0].description().contains("test1"));
+  // assert(st[0].source_file().ends_with("current_no_args.pass.cpp"));
+  // assert(st[0].source_line() == test1_line);
+  assert(st[1]);
+  assert(st[1].native_handle());
+  // assert(st[1].description().contains("test2"));
+  // assert(st[1].source_file().ends_with("current_no_args.pass.cpp"));
+  // assert(st[1].source_line() == test2_line);
+  assert(st[2]);
+  assert(st[2].native_handle());
+  // assert(st[2].description().contains("test_current"));
+  // assert(st[2].source_file().ends_with("current_no_args.pass.cpp"));
+  // assert(st[2].source_line() == main_line);
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  static_assert(noexcept(std::stacktrace::current()));
+  test_current();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
new file mode 100644
index 0000000000000..d0bf282d5fccd
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  static basic_stacktrace current(size_type skip,
+                                  const allocator_type& alloc = allocator_type()) noexcept;   [2]
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+/*
+  Let t be a stacktrace as-if obtained via basic_stacktrace::current(alloc). Let n be t.size().
+  Returns: A basic_stacktrace object where frames_ is direct-non-list-initialized from arguments
+  t.begin() + min(n, skip), t.end(), and alloc, or an empty basic_stacktrace object if the
+  initialization of frames_ failed.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip() {
+  // Use default allocator for simplicity; alloc is covered above
+  auto st_skip0 = std::stacktrace::current();
+  assert(st_skip0.size() >= 2);
+  auto st_skip1 = std::stacktrace::current(1);
+  assert(st_skip1.size() >= 1);
+  assert(st_skip0.size() == st_skip1.size() + 1);
+  assert(st_skip0[1] == st_skip1[0]);
+  auto st_skip_many = std::stacktrace::current(1 << 20);
+  assert(st_skip_many.empty());
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  static_assert(noexcept(std::stacktrace::current(0)));
+  test_current_with_skip();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
new file mode 100644
index 0000000000000..300f95e8e97b3
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: no-localization
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  static basic_stacktrace current(size_type skip, size_type max_depth,
+                                  const allocator_type& alloc = allocator_type()) noexcept;
+*/
+
+#include <__config_site>
+#if _LIBCPP_HAS_LOCALIZATION
+
+#  include <cassert>
+#  include <iostream>
+#  include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
+  // current stack is: [this function, main, (possibly something else, such as libc _start)]
+  // so it's probably 3 functions deep -- but certainly at least 2 deep.
+  std::stacktrace_entry entry;
+  {
+    auto st1 = std::stacktrace::current(0);
+    std::cout << st1 << '\n';
+    assert(st1.size() >= 2);
+    auto it1 = st1.begin();
+    ++it1;
+    entry = *it1; // represents our caller, `main`
+  }
+
+  // get current trace again, but skip the 1st
+  auto st2 = std::stacktrace::current(1);
+  std::cout << st2 << '\n';
+  assert(st2.size() >= 1);
+  auto it2 = st2.begin();
+  assert(*it2 == entry);
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  static_assert(noexcept(std::stacktrace::current(0, 0)));
+  test_current_with_skip_depth();
+  return 0;
+}
+
+#else
+int main() { return 0; }
+#endif // _LIBCPP_HAS_LOCALIZATION
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
new file mode 100644
index 0000000000000..a659a515231f4
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  basic_stacktrace(basic_stacktrace&& other) noexcept;                                    
+  basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);                
+  basic_stacktrace& operator=(basic_stacktrace&& other)
+    noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+      allocator_traits<Allocator>::is_always_equal::value);                               
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <utility>
+
+#include "../test_allocs.h"
+
+void test_move_construct() {
+  auto a = std::stacktrace::current();
+  std::stacktrace b{a};
+  assert(a == b);
+}
+
+void test_move_assign() {
+  {
+    using A =
+        TestAlloc<std::stacktrace_entry,
+                  /*_KNoExCtors=*/true,
+                  /*_KNoExAlloc=*/true,
+                  /*_KPropagate=*/false,
+                  /*_KAlwaysEqual=*/false>;
+    auto s0 = std::basic_stacktrace<A>::current();
+    std::basic_stacktrace<A> s1{s0};
+    std::basic_stacktrace<A> s2(std::move(s0));
+    assert(s1 == s2);
+    auto a1 = s1.get_allocator();
+    auto a2 = s2.get_allocator();
+    // Allocator should not propagate
+    assert(a1 != a2);
+  }
+  {
+    using A =
+        TestAlloc<std::stacktrace_entry,
+                  /*_KNoExCtors=*/true,
+                  /*_KNoExAlloc=*/true,
+                  /*_KPropagate=*/true,
+                  /*_KAlwaysEqual=*/false>;
+    auto s0 = std::basic_stacktrace<A>::current();
+    std::basic_stacktrace<A> s1{s0};
+    std::basic_stacktrace<A> s2(std::move(s0));
+    auto a1 = s1.get_allocator();
+    auto a2 = s2.get_allocator();
+    // Allocator should propagate
+    assert(a1 == a2);
+  }
+  {
+    using A =
+        TestAlloc<std::stacktrace_entry,
+                  /*_KNoExCtors=*/true,
+                  /*_KNoExAlloc=*/true,
+                  /*_KPropagate=*/false,
+                  /*_KAlwaysEqual=*/true>;
+    auto s0 = std::basic_stacktrace<A>::current();
+    std::basic_stacktrace<A> s1{s0};
+    std::basic_stacktrace<A> s2(std::move(s0));
+    auto a1 = s1.get_allocator();
+    auto a2 = s2.get_allocator();
+    // Allocator should propagate
+    assert(a1 == a2);
+  }
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  test_move_construct();
+  test_move_assign();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
new file mode 100644
index 0000000000000..2b5cdc2256d9c
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.6) Hash Support
+
+  template<> struct hash<stacktrace_entry>;                                 [1]
+  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;       [2]
+
+  The specializations are enabled ([unord.hash]).
+*/
+
+#include <cassert>
+#include <functional>
+#include <stacktrace>
+
+int main(int, char**) {
+  std::stacktrace trace; // initially empty
+  auto hash_val_empty    = std::hash<std::stacktrace>()(trace);
+  trace                  = /* reassign */ std::stacktrace::current();
+  auto hash_val_nonempty = std::hash<std::stacktrace>()(trace);
+  assert(hash_val_empty != hash_val_nonempty);
+
+  std::stacktrace_entry empty_entry;
+  assert(std::hash<std::stacktrace_entry>()(empty_entry) == 0);
+  auto nonempty_entry = trace[0];
+  assert(std::hash<std::stacktrace_entry>()(nonempty_entry) != 0);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
new file mode 100644
index 0000000000000..6307ced797302
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.5) Modifiers [stacktrace.basic.mod]
+
+  template<class Allocator>
+  void swap(basic_stacktrace& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+      allocator_traits<Allocator>::is_always_equal::value);
+
+  Effects: Exchanges the contents of *this and other.
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+#include "../test_allocs.h"
+
+int main(int, char**) {
+  std::stacktrace empty;
+  auto a = std::stacktrace::current();
+  std::stacktrace b(empty);
+  assert(!a.empty());
+  assert(b.empty());
+
+  a.swap(b);
+  assert(a.empty());
+  assert(!b.empty());
+
+  // Check `noexcept`: `swap` is noexcept if either:
+  //   (1) the allocator propagates on swap
+  //   (2) if instances of that allocator type are always equal.
+
+  // `AllocPropagate` satisfies the first (but not the second); stacktrace swap should be noexcept
+  AllocPropagate<std::stacktrace_entry> prop1;
+  AllocPropagate<std::stacktrace_entry> prop2;
+  auto prop_st1 = std::basic_stacktrace<decltype(prop1)>(prop1);
+  auto prop_st2 = std::basic_stacktrace<decltype(prop2)>(prop2);
+  static_assert(noexcept(prop_st1.swap(prop_st2)));
+
+  // `AllocNoPropagate` satisfies neither; stacktrace swap should not be noexcept
+  AllocNoPropagate<std::stacktrace_entry> no_prop1;
+  AllocNoPropagate<std::stacktrace_entry> no_prop2;
+  auto no_prop_st1 = std::basic_stacktrace<decltype(no_prop1)>(no_prop1);
+  auto no_prop_st2 = std::basic_stacktrace<decltype(no_prop2)>(no_prop2);
+  static_assert(!noexcept(no_prop_st1.swap(no_prop_st2)));
+
+  // `AllocAlwaysEqual` satisfies second; stacktrace swap should be noexcept
+  AllocAlwaysEqual<std::stacktrace_entry> always_eq1;
+  AllocAlwaysEqual<std::stacktrace_entry> always_eq2;
+  auto always_eq_st1 = std::basic_stacktrace<decltype(always_eq1)>(always_eq1);
+  auto always_eq_st2 = std::basic_stacktrace<decltype(always_eq2)>(always_eq2);
+  static_assert(noexcept(always_eq_st1.swap(always_eq_st2)));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.pass.cpp
new file mode 100644
index 0000000000000..790d408407336
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: no-localization
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.6) Non-member functions
+
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  template<class Allocator>
+    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+*/
+
+#include <__config_site>
+#if _LIBCPP_HAS_LOCALIZATION
+
+#  include <cassert>
+#  include <sstream>
+#  include <stacktrace>
+
+int main(int, char**) {
+  auto a = std::stacktrace::current();
+
+  std::stringstream entry_os;
+  entry_os << a[0];
+  assert(entry_os.str() == std::to_string(a[0]));
+
+  std::stringstream trace_os;
+  trace_os << a;
+  assert(trace_os.str() == std::to_string(a));
+
+  return 0;
+}
+
+#else
+int main() { return 0; }
+#endif // _LIBCPP_HAS_LOCALIZATION
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp
new file mode 100644
index 0000000000000..792441921adbd
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.6) Non-member functions
+
+  template<class Allocator>
+    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+      noexcept(noexcept(a.swap(b)));
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+#include "../test_allocs.h"
+
+int main(int, char**) {
+  std::stacktrace empty;
+  auto a = std::stacktrace::current();
+  std::stacktrace b(empty);
+  assert(!a.empty());
+  assert(b.empty());
+
+  std::swap(a, b);
+  assert(a.empty());
+  assert(!b.empty());
+
+  // `AllocPropagate` satisfies the first (but not the second); stacktrace swap should be noexcept
+  AllocPropagate<std::stacktrace_entry> prop1;
+  AllocPropagate<std::stacktrace_entry> prop2;
+  auto prop_st1 = std::basic_stacktrace<decltype(prop1)>(prop1);
+  auto prop_st2 = std::basic_stacktrace<decltype(prop2)>(prop2);
+  static_assert(noexcept(std::swap(prop_st1, prop_st2)));
+
+  // `AllocNoPropagate` satisfies neither; stacktrace swap should not be noexcept
+  AllocNoPropagate<std::stacktrace_entry> no_prop1;
+  AllocNoPropagate<std::stacktrace_entry> no_prop2;
+  auto no_prop_st1 = std::basic_stacktrace<decltype(no_prop1)>(no_prop1);
+  auto no_prop_st2 = std::basic_stacktrace<decltype(no_prop2)>(no_prop2);
+  static_assert(!noexcept(std::swap(no_prop_st1, no_prop_st2)));
+
+  // `AllocAlwaysEqual` satisfies second; stacktrace swap should be noexcept
+  AllocAlwaysEqual<std::stacktrace_entry> always_eq1;
+  AllocAlwaysEqual<std::stacktrace_entry> always_eq2;
+  auto always_eq_st1 = std::basic_stacktrace<decltype(always_eq1)>(always_eq1);
+  auto always_eq_st2 = std::basic_stacktrace<decltype(always_eq2)>(always_eq2);
+  static_assert(noexcept(std::swap(always_eq_st1, always_eq_st2)));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.pass.cpp
new file mode 100644
index 0000000000000..4630622b7bd8e
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: no-localization
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.6) Non-member functions
+
+  string to_string(const stacktrace_entry& f);
+
+  template<class Allocator>
+    string to_string(const basic_stacktrace<Allocator>& st);
+*/
+
+#include <__config_site>
+#if _LIBCPP_HAS_LOCALIZATION
+
+#  include <cassert>
+#  include <iostream>
+#  include <stacktrace>
+
+int main(int, char**) {
+  auto a    = std::stacktrace::current();
+  auto astr = std::to_string(a);
+  std::cerr << astr << '\n';
+
+  // assert(std::to_string(a[0]).contains("main"));
+  assert(std::to_string(a[0]).contains("to_string.pass"));
+
+  // assert(std::to_string(a).contains("main"));
+  assert(std::to_string(a).contains("to_string.pass"));
+
+  return 0;
+}
+
+#else
+int main() { return 0; }
+#endif // _LIBCPP_HAS_LOCALIZATION
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
new file mode 100644
index 0000000000000..6084f3995b2c0
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_reference at(size_type) const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  auto st = test3();
+
+  assert(st.at(0));
+  assert(st.at(1));
+  assert(st.at(2));
+  assert(st.at(3));
+
+  auto f0 = st.at(0);
+  auto f1 = st.at(1);
+  auto f2 = st.at(2);
+  auto f3 = st.at(3);
+
+  auto it = st.begin();
+  assert(*it++ == f0);
+  assert(it != st.end());
+  assert(*it++ == f1);
+  assert(it != st.end());
+  assert(*it++ == f2);
+  assert(it != st.end());
+  assert(*it++ == f3);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
new file mode 100644
index 0000000000000..3206879964134
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_iterator begin() const noexcept;
+  const_iterator end() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(noexcept(st.begin()));
+  static_assert(noexcept(st.end()));
+  static_assert(std::random_access_iterator<decltype(st.begin())>);
+  assert(st.begin() == st.end());
+  // no longer empty:
+  st = test3();
+  assert(!st.empty());
+  assert(st.begin() != st.end());
+  auto f0 = st[0];
+  auto it = st.begin();
+  assert(*it == f0);
+  assert(it != st.end());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
new file mode 100644
index 0000000000000..a3628e454b9bc
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_iterator cbegin() const noexcept;
+  const_iterator cend() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(noexcept(st.cbegin()));
+  static_assert(noexcept(st.cend()));
+  static_assert(std::random_access_iterator<decltype(st.cbegin())>);
+  assert(st.cbegin() == st.cend());
+  // no longer empty:
+  st = test3();
+  assert(st.cbegin() != st.cend());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
new file mode 100644
index 0000000000000..b211fb753a688
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_reverse_iterator crbegin() const noexcept;
+  const_reverse_iterator crend() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(noexcept(st.crbegin()));
+  static_assert(noexcept(st.crend()));
+  static_assert(std::random_access_iterator<decltype(st.crbegin())>);
+  assert(st.crbegin() == st.crend());
+  // no longer empty:
+  st = test3();
+  assert(st.crbegin() != st.crend());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
new file mode 100644
index 0000000000000..837e0bbf80acf
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  bool empty() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(noexcept(st.empty()));
+  assert(st.empty());
+  st = test3();
+  assert(!st.empty());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp
new file mode 100644
index 0000000000000..243073efae0bd
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  allocator_type get_allocator() const noexcept;
+*/
+
+#include <cassert>
+#include <concepts>
+#include <stacktrace>
+
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(noexcept(st.get_allocator()));
+  static_assert(std::same_as<decltype(st.get_allocator()), std::stacktrace::allocator_type>);
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
new file mode 100644
index 0000000000000..3ba11885eff0b
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  size_type max_size() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <vector>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(noexcept(st.max_size()));
+  assert(st.max_size() == (std::vector<std::stacktrace_entry>().max_size()));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
new file mode 100644
index 0000000000000..bce77a7047f40
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_reference operator[](size_type) const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  auto st = test3();
+  assert(st[0]);
+  assert(st[1]);
+  assert(st[2]);
+  assert(st[3]);
+
+  auto f0 = st[0];
+  auto f1 = st[1];
+  auto f2 = st[2];
+  auto f3 = st[3];
+
+  auto it = st.begin();
+  assert(*it++ == f0);
+  assert(it != st.end());
+  assert(*it++ == f1);
+  assert(it != st.end());
+  assert(*it++ == f2);
+  assert(it != st.end());
+  assert(*it++ == f3);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
new file mode 100644
index 0000000000000..471367e19436f
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_reverse_iterator rbegin() const noexcept;
+  const_reverse_iterator rend() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(noexcept(st.rbegin()));
+  static_assert(noexcept(st.rend()));
+  static_assert(std::random_access_iterator<decltype(st.rbegin())>);
+  assert(st.rbegin() == st.rend());
+  // no longer empty:
+  st      = test3();
+  auto it = st.rbegin();
+  assert(it != st.rend());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
new file mode 100644
index 0000000000000..03e003c6d2c0c
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  size_type size() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(noexcept(st.size()));
+  assert(st.size() == 0);
+  st = test3();
+  assert(st.size() > 0);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
new file mode 100644
index 0000000000000..270ea3494e0bb
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
@@ -0,0 +1,143 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.4) Class template basic_stacktrace [stacktrace.basic]
+  (19.6.4.1) Overview                      [stacktrace.basic.overview]
+
+namespace std {
+  template<class Allocator>
+  class basic_stacktrace {
+  public:
+    using value_type = stacktrace_entry;
+    using const_reference = const value_type&;
+    using reference = value_type&;
+    using const_iterator = implementation-defined;  // see [stacktrace.basic.obs]
+    using iterator = const_iterator;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+    using difference_type = implementation-defined;
+    using size_type = implementation-defined;
+    using allocator_type = Allocator;
+
+    // (19.6.4.2)
+    // [stacktrace.basic.cons], creation and assignment
+    static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+    static basic_stacktrace current(size_type skip,
+                                    const allocator_type& alloc = allocator_type()) noexcept;
+    static basic_stacktrace current(size_type skip, size_type max_depth,
+                                    const allocator_type& alloc = allocator_type()) noexcept;
+
+    basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+    explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+
+    basic_stacktrace(const basic_stacktrace& other);
+    basic_stacktrace(basic_stacktrace&& other) noexcept;
+    basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+    basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+    basic_stacktrace& operator=(const basic_stacktrace& other);
+    basic_stacktrace& operator=(basic_stacktrace&& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+        allocator_traits<Allocator>::is_always_equal::value);
+
+    ~basic_stacktrace();
+
+    // (19.6.4.3)
+    // [stacktrace.basic.obs], observers
+    allocator_type get_allocator() const noexcept;
+
+    const_iterator begin() const noexcept;
+    const_iterator end() const noexcept;
+    const_reverse_iterator rbegin() const noexcept;
+    const_reverse_iterator rend() const noexcept;
+
+    const_iterator cbegin() const noexcept;
+    const_iterator cend() const noexcept;
+    const_reverse_iterator crbegin() const noexcept;
+    const_reverse_iterator crend() const noexcept;
+
+    bool empty() const noexcept;
+    size_type size() const noexcept;
+    size_type max_size() const noexcept;
+
+    const_reference operator[](size_type) const;
+    const_reference at(size_type) const;
+    
+    // (19.6.4.4)
+    // [stacktrace.basic.cmp], comparisons
+    template<class Allocator2>
+    friend bool operator==(const basic_stacktrace& x,
+                           const basic_stacktrace<Allocator2>& y) noexcept;
+    template<class Allocator2>
+    friend strong_ordering operator<=>(const basic_stacktrace& x,
+                                       const basic_stacktrace<Allocator2>& y) noexcept;
+
+    // (19.6.4.5)
+    // [stacktrace.basic.mod], modifiers
+    void swap(basic_stacktrace& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+        allocator_traits<Allocator>::is_always_equal::value);
+
+  private:
+    vector<value_type, allocator_type> frames_;         // exposition only
+  };
+
+  // (19.6.4.6)
+  // [stacktrace.basic.nonmem], non-member functions
+
+  template<class Allocator>
+    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+      noexcept(noexcept(a.swap(b)));
+
+  string to_string(const stacktrace_entry& f);
+
+  template<class Allocator>
+    string to_string(const basic_stacktrace<Allocator>& st);
+
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  template<class Allocator>
+    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+}
+
+*/
+
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <stacktrace>
+#include <type_traits>
+
+int main(int, char**) {
+  // This test will only verify these member types exist and are
+  // defined as expected; their actual behavior is tested in another .cpp.
+
+  using A = std::allocator<std::stacktrace_entry>;
+  using S = std::basic_stacktrace<A>;
+
+  static_assert(std::is_same_v<S::value_type, std::stacktrace_entry>);
+  static_assert(std::is_same_v<S::const_reference, std::stacktrace_entry const&>);
+  static_assert(std::is_same_v<S::reference, std::stacktrace_entry&>);
+
+  static_assert(std::forward_iterator<S::const_iterator>);
+  static_assert(std::forward_iterator<S::iterator>);
+  using CRI = S::const_reverse_iterator;
+  static_assert(std::is_same_v<CRI, decltype(S(A()).rbegin())>);
+  static_assert(std::is_same_v<CRI, decltype(S(A()).crbegin())>);
+
+  using IterT = S::iterator;
+  using DiffT = S::difference_type;
+  static_assert(std::is_same_v<IterT, decltype(IterT() + DiffT())>);
+
+  static_assert(std::is_integral_v<S::size_type>);
+  static_assert(std::is_same_v<S::allocator_type, A>);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
new file mode 100644
index 0000000000000..0b05c3b09fcfd
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.3.5) Comparison [stacktrace.entry.cmp]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.cmp], comparison
+    friend constexpr bool operator==(const stacktrace_entry& x,
+                                     const stacktrace_entry& y) noexcept;
+*/
+
+#include <cassert>
+#include <cstdint>
+#include <stacktrace>
+
+namespace {
+int func1() { return 41; }
+int func2() { return 42; }
+} // namespace
+
+int main(int, char**) {
+  std::stacktrace_entry a;
+  std::stacktrace_entry b;
+  std::stacktrace_entry c;
+
+  *(uintptr_t*)(&a) = uintptr_t(&func1);
+  *(uintptr_t*)(&b) = uintptr_t(&func1);
+  *(uintptr_t*)(&c) = uintptr_t(&func2);
+
+  static_assert(noexcept(a == b));
+
+  assert(a == b);
+  assert(a != c);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
new file mode 100644
index 0000000000000..7f0340f62687e
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.3.5) Comparison [stacktrace.entry.cmp]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.cmp], comparison
+    friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+                                                 const stacktrace_entry& y) noexcept;
+*/
+
+#include <cassert>
+#include <cstdint>
+#include <stacktrace>
+#include <utility>
+
+namespace {
+int func1() { return 41; }
+int func2() { return 42; }
+} // namespace
+
+int main(int, char**) {
+  auto addr1 = uintptr_t(&func1);
+  auto addr2 = uintptr_t(&func2);
+  assert(addr1 != addr2);
+  if (addr1 > addr2) {
+    std::swap(addr1, addr2);
+  }
+
+  std::stacktrace_entry a;
+  std::stacktrace_entry b;
+  std::stacktrace_entry c;
+
+  *(uintptr_t*)(&a) = uintptr_t(addr1);
+  *(uintptr_t*)(&b) = uintptr_t(addr1);
+  *(uintptr_t*)(&c) = uintptr_t(addr2);
+
+  static_assert(noexcept(a <=> b));
+
+  assert(std::strong_ordering::equal == (a <=> b));
+  assert(std::strong_ordering::equivalent == (a <=> b));
+  assert(std::strong_ordering::greater == (c <=> a));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp
new file mode 100644
index 0000000000000..93198a4ac64a4
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+  class stacktrace_entry {
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
+}
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
+
+  auto e1 = std::stacktrace::current()[0];
+  std::stacktrace_entry e2;
+  static_assert(noexcept(e2 = e1));
+  e2 = e1;
+  assert(e2 == e1);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
new file mode 100644
index 0000000000000..822047c65d44b
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+  class stacktrace_entry {
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
+}
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
+
+  auto e1 = std::stacktrace::current()[0];
+  static_assert(noexcept(std::stacktrace_entry(e1)));
+  std::stacktrace_entry e2(e1);
+  assert(e2 == e1);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp
new file mode 100644
index 0000000000000..6b6f0db515971
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+  class stacktrace_entry {
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry() noexcept;
+}
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+int main(int, char**) {
+  static_assert(std::is_nothrow_default_constructible_v<std::stacktrace_entry>);
+
+  std::stacktrace_entry entry;
+  // "Postconditions: *this is empty."
+  assert(!entry);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp
new file mode 100644
index 0000000000000..1975e4afbae15
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.3.3) Observers [stacktrace.entry.obs]
+
+namespace std {
+  class stacktrace_entry {
+    // [stacktrace.entry.obs], observers
+    constexpr native_handle_type native_handle() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) noexcept {
+  std::stacktrace_entry e;
+  static_assert(noexcept(e.native_handle()));
+  assert(e.native_handle() == 0);
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp
new file mode 100644
index 0000000000000..5d58fe1e80923
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+  (19.6.3.3) Observers [stacktrace.entry.obs]
+
+namespace std {
+  class stacktrace_entry {
+    // [stacktrace.entry.obs], observers
+    constexpr explicit operator bool() const noexcept;
+*/
+
+#include <cassert>
+#include <cstdint>
+#include <stacktrace>
+
+int main(int, char**) {
+  std::stacktrace_entry e;
+  // "Returns: false if and only if *this is empty."
+  assert(!e);
+  // Now set addr to something nonzero
+  *(uintptr_t*)(&e) = uintptr_t(&main);
+  assert(e.native_handle() == uintptr_t(&main));
+  assert(e);
+
+  static_assert(noexcept(bool(e)));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
new file mode 100644
index 0000000000000..19783b031dbf0
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+#include <stacktrace>
+
+#include <cassert>
+#include <concepts>
+#include <cstddef>
+
+/*
+  (19.6.3) Class stacktrace_entry         [stacktrace.entry]
+  (19.6.3.1) Overview                     [stacktrace.entry.overview]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    using native_handle_type = implementation-defined;
+
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry() noexcept;                                          
+    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;             
+    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;  
+    ~stacktrace_entry();                                                            
+    
+    // [stacktrace.entry.obs], observers
+    constexpr native_handle_type native_handle() const noexcept;                    
+    constexpr explicit operator bool() const noexcept;                              
+    
+    // [stacktrace.entry.query], query
+    string description() const;                                                     
+    string source_file() const;                                                     
+    uint_least32_t source_line() const;                                             
+    
+    // [stacktrace.entry.cmp], comparison
+    friend constexpr bool operator==(const stacktrace_entry& x,
+                                     const stacktrace_entry& y) noexcept;           
+    friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+                                                 const stacktrace_entry& y) noexcept;
+  };
+}
+*/
+
+int main(int, char**) {
+  // [stacktrace.entry.overview]
+  // "The class stacktrace_entry models regular ([concepts.object])
+  // and three_way_comparable<strong_ordering> ([cmp.concept])."
+  static_assert(std::regular<std::stacktrace_entry>);
+  static_assert(std::three_way_comparable<std::stacktrace_entry, std::strong_ordering>);
+
+  // using native_handle_type = implementation-defined;
+  static_assert(sizeof(std::stacktrace_entry::native_handle_type) >= sizeof(void*));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query/description.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query/description.pass.cpp
new file mode 100644
index 0000000000000..a6f9be7ab92c4
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query/description.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+    (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.query], query
+    string description() const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <string>
+
+int main(int, char**) {
+  std::stacktrace_entry e;
+  auto desc = e.description();
+  assert(desc.empty());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query/source_file.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_file.pass.cpp
new file mode 100644
index 0000000000000..83b7784038a73
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_file.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+    (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.query], query
+    string source_file() const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <string>
+
+int main(int, char**) {
+  std::stacktrace_entry e;
+  auto src = e.source_file();
+  assert(src.empty());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query/source_line.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_line.pass.cpp
new file mode 100644
index 0000000000000..70b04ada147af
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_line.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+    (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.query], query
+    uint_least32_t source_line() const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) {
+  std::stacktrace_entry e;
+  assert(e.source_line() == 0);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp
new file mode 100644
index 0000000000000..f733ae5786881
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+// UNSUPPORTED: asan, msan, tsan, hwasan, sanitizer-new-delete
+// XFAIL: availability-stacktrace-missing
+
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <memory>
+#include <stacktrace>
+
+#include "test_allocs.h"
+
+/*
+ * This file includes tests which ensure any allocations performed by `basic_stacktrace`
+ * are done via the user-provided allocator.  We intercept the usual ways to allocate,
+ * counting the number of calls, through and not through the allocator.
+ * (This won't work properly with sanitizers, hence the `UNSUPPORTED` above.)
+ */
+
+unsigned new_count      = 0;
+unsigned del_count      = 0;
+unsigned custom_alloc   = 0;
+unsigned custom_dealloc = 0;
+
+void* operator new(size_t sz) {
+  ++new_count;
+  auto* ret = malloc(sz);
+  return ret;
+}
+
+void* operator new[](size_t sz) {
+  ++new_count;
+  auto* ret = malloc(sz);
+  return ret;
+}
+
+void operator delete(void* ptr) noexcept {
+  ++del_count;
+  free(ptr);
+}
+
+void operator delete(void* ptr, size_t) noexcept {
+  ++del_count;
+  free(ptr);
+}
+
+void operator delete[](void* ptr) noexcept {
+  ++del_count;
+  free(ptr);
+}
+
+void operator delete[](void* ptr, size_t) noexcept {
+  ++del_count;
+  free(ptr);
+}
+
+template <typename T>
+struct test_alloc : std::allocator<T> {
+  using base = std::allocator<T>;
+
+  template <typename U>
+  struct rebind {
+    using other = test_alloc<U>;
+  };
+
+  T* allocate(size_t n) {
+    ++custom_alloc;
+    auto* ret = base::allocate(n);
+    return ret;
+  }
+
+  std::allocation_result<T*, size_t> allocate_at_least(size_t n) {
+    ++custom_alloc;
+    auto ret = base::allocate_at_least(n);
+    return ret;
+  }
+
+  void deallocate(T* p, size_t n) {
+    ++custom_dealloc;
+    base::deallocate(p, n);
+  }
+};
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  (void)std::stacktrace::current();
+
+  // Clear these counters in case anything was created/deleted prior to `main`,
+  // and in case taking a stacktrace involved initialization of objects in bss
+  // or some other space.
+  new_count = del_count = 0;
+
+  {
+    using A = test_alloc<std::stacktrace_entry>;
+    A alloc;
+    auto st = std::basic_stacktrace<A>::current(alloc);
+    // Ensure allocator was called at some point
+    assert(custom_alloc > 0);
+    // Exit this scope to destroy stacktrace and allocator
+  }
+
+  assert(custom_alloc == new_count);      // All objects should have come from allocator,
+  assert(custom_alloc == custom_dealloc); // and all allocations should be deallocated
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/simple.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/simple.pass.cpp
new file mode 100644
index 0000000000000..69f67e244742a
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/simple.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+// UNSUPPORTED: no-localization
+// XFAIL: availability-stacktrace-missing
+
+/*
+This isn't really a test; it simply takes a stacktrace and prints to stdout,
+so we can see what a stacktrace actually contains and looks like when printed.
+*/
+
+#include <__config_site>
+#if _LIBCPP_HAS_LOCALIZATION
+
+#  include <cassert>
+#  include <iostream>
+#  include <stacktrace>
+
+int main(int, char**) {
+  std::cout << std::stacktrace::current() << '\n';
+  return 0;
+}
+
+#else
+int main() { return 0; }
+#endif // _LIBCPP_HAS_LOCALIZATION
diff --git a/libcxx/test/std/diagnostics/stacktrace/test_allocs.h b/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
new file mode 100644
index 0000000000000..682874dcdba25
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+/*
+Allocator class useful for testing various propagation, always-equal scenarios.
+*/
+
+#ifndef _LIBCPP_STACKTRACE_TEST_ALLOCS_H
+#define _LIBCPP_STACKTRACE_TEST_ALLOCS_H
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+
+template <typename T, bool _KNoExCtors, bool _KNoExAlloc, bool _KPropagate, bool _KAlwaysEqual>
+struct TestAlloc {
+  using size_type     = size_t;
+  using value_type    = T;
+  using pointer       = T*;
+  using const_pointer = T const*;
+
+  using Self = TestAlloc<T, _KNoExCtors, _KNoExAlloc, _KPropagate, _KAlwaysEqual>;
+
+  template <typename U>
+  using Other = TestAlloc<U, _KNoExCtors, _KNoExAlloc, _KPropagate, _KAlwaysEqual>;
+
+  using propagate_on_container_copy_assignment = typename std::bool_constant<_KPropagate>;
+  using propagate_on_container_move_assignment = typename std::bool_constant<_KPropagate>;
+  using propagate_on_container_swap            = typename std::bool_constant<_KPropagate>;
+  using is_always_equal                        = typename std::bool_constant<_KAlwaysEqual>;
+
+  auto select_on_container_copy_construction(this auto& self) { return _KPropagate ? self : Self(); }
+
+  template <typename U>
+  struct rebind {
+    using other = Other<U>;
+  };
+
+  static std::shared_ptr<std::allocator<std::byte>> new_alloc() {
+    return std::make_shared<std::allocator<std::byte>>();
+  }
+
+  static std::shared_ptr<std::allocator<std::byte>> global_alloc() {
+    static auto ret = new_alloc();
+    return ret;
+  }
+
+  /** Type-erased allocator used for servicing allocate and deallocate.
+  Two `TestAlloc`'s are equal IFF they contain the same alloc pointer.
+  Always-equal `TestAlloc`'s get a pointer to a shared `global_alloc`. */
+  std::shared_ptr<std::allocator<std::byte>> alloc_;
+
+  /** Instances are equal IFF they have the same alloc pointer (even if this is "always_equals",
+  since such instances point to the global alloc). */
+  bool operator==(auto const& rhs) const noexcept { return alloc_.get() == rhs.alloc_.get(); }
+
+  /** Construct with a new alloc, or, if always-equal, the global alloc. */
+  TestAlloc() noexcept(_KNoExCtors) : alloc_(_KAlwaysEqual ? global_alloc() : new_alloc()) {}
+
+  template <typename U>
+  TestAlloc(Other<U> const& rhs) : alloc_(rhs.alloc_) {}
+
+  template <typename U>
+  TestAlloc& operator=(Other<U> const& rhs) {
+    alloc_ = rhs.alloc_;
+  }
+
+  std::allocator<T>& alloc() { return *(std::allocator<T>*)alloc_.get(); }
+
+  T* allocate(size_t n) noexcept(_KNoExAlloc) { return alloc().allocate(n); }
+  auto allocate_at_least(size_t n) noexcept(_KNoExAlloc) { return alloc().allocate_at_least(n); }
+  void deallocate(T* ptr, size_t n) noexcept(_KNoExAlloc) { return alloc().deallocate(ptr, n); }
+};
+
+// For convenience and readability:
+
+template <typename T>
+using AllocPropagate =
+    TestAlloc<T,
+              /*_KNoExCtors=*/true,
+              /*_KNoExAlloc=*/true,
+              /*_KPropagate=*/true,
+              /*_KAlwaysEqual=*/false>;
+
+template <typename T>
+using AllocNoPropagate =
+    TestAlloc<T,
+              /*_KNoExCtors=*/true,
+              /*_KNoExAlloc=*/true,
+              /*_KPropagate=*/false,
+              /*_KAlwaysEqual=*/false>;
+
+template <typename T>
+using AllocAlwaysEqual =
+    TestAlloc<T,
+              /*_KNoExCtors=*/true,
+              /*_KNoExAlloc=*/true,
+              /*_KPropagate=*/true,
+              /*_KAlwaysEqual=*/true>;
+
+#endif
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
new file mode 100644
index 0000000000000..3ffc575cb225a
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <stacktrace>
+
+// Test the feature test macros defined by <stacktrace>
+
+// clang-format off
+
+#include <stacktrace>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_formatters
+#    error "__cpp_lib_formatters should not be defined before c++23"
+#  endif
+
+#  ifdef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifdef __cpp_lib_formatters
+#    error "__cpp_lib_formatters should not be defined before c++23"
+#  endif
+
+#  ifdef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_formatters
+#    error "__cpp_lib_formatters should not be defined before c++23"
+#  endif
+
+#  ifdef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifdef __cpp_lib_formatters
+#    error "__cpp_lib_formatters should not be defined before c++23"
+#  endif
+
+#  ifdef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_formatters
+#      error "__cpp_lib_formatters should be defined in c++23"
+#    endif
+#    if __cpp_lib_formatters != 202302L
+#      error "__cpp_lib_formatters should have the value 202302L in c++23"
+#    endif
+#  else
+#    ifdef __cpp_lib_formatters
+#      error "__cpp_lib_formatters should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#  if !defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+#    ifndef __cpp_lib_stacktrace
+#      error "__cpp_lib_stacktrace should be defined in c++23"
+#    endif
+#    if __cpp_lib_stacktrace != 202011L
+#      error "__cpp_lib_stacktrace should have the value 202011L in c++23"
+#    endif
+#  else
+#    ifdef __cpp_lib_stacktrace
+#      error "__cpp_lib_stacktrace should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE' is not met!"
+#    endif
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_formatters
+#      error "__cpp_lib_formatters should be defined in c++26"
+#    endif
+#    if __cpp_lib_formatters != 202302L
+#      error "__cpp_lib_formatters should have the value 202302L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_formatters
+#      error "__cpp_lib_formatters should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#  if !defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+#    ifndef __cpp_lib_stacktrace
+#      error "__cpp_lib_stacktrace should be defined in c++26"
+#    endif
+#    if __cpp_lib_stacktrace != 202011L
+#      error "__cpp_lib_stacktrace should have the value 202011L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_stacktrace
+#      error "__cpp_lib_stacktrace should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE' is not met!"
+#    endif
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 05af1fb0cf14b..ff323e06febf1 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -5961,7 +5961,7 @@
 #    error "__cpp_lib_sstream_from_string_view should not be defined before c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
+#  if !defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE
 #    ifndef __cpp_lib_stacktrace
 #      error "__cpp_lib_stacktrace should be defined in c++23"
 #    endif
@@ -5970,7 +5970,7 @@
 #    endif
 #  else
 #    ifdef __cpp_lib_stacktrace
-#      error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
+#      error "__cpp_lib_stacktrace should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE' is not met!"
 #    endif
 #  endif
 
@@ -7923,7 +7923,7 @@
 #    error "__cpp_lib_sstream_from_string_view should have the value 202306L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
+#  if !defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE
 #    ifndef __cpp_lib_stacktrace
 #      error "__cpp_lib_stacktrace should be defined in c++26"
 #    endif
@@ -7932,7 +7932,7 @@
 #    endif
 #  else
 #    ifdef __cpp_lib_stacktrace
-#      error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
+#      error "__cpp_lib_stacktrace should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE' is not met!"
 #    endif
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index c4e1600572456..5f00c1f994662 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -360,6 +360,24 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOINLINE
 #endif
 
+// Disables tail-call optimization for "outbound" calls
+// performed in the function annotated with this attribute.
+#if __has_cpp_attribute(_Clang::__disable_tail_calls__)
+#define TEST_NO_TAIL_CALLS_OUT [[_Clang::__disable_tail_calls__]]
+#elif __has_cpp_attribute(__gnu__::__optimize__)
+#define TEST_NO_TAIL_CALLS_OUT [[__gnu__::__optimize__("no-optimize-sibling-calls")]]
+#else
+#define TEST_NO_TAIL_CALLS_OUT
+#endif
+
+// Disables tail-call optimization for "inbound" calls -- that is,
+// calls from some other function calling the one having this attribute.
+#if __has_cpp_attribute(_Clang::__not_tail_called__)
+#define TEST_NO_TAIL_CALLS_IN [[_Clang::__not_tail_called__]]
+#else
+#define TEST_NO_TAIL_CALLS_IN
+#endif
+
 #ifdef _WIN32
 #define TEST_NOT_WIN32(...) ((void)0)
 #else
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f6f252751b3e3..bb549d2510b47 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1318,7 +1318,8 @@ def add_version_header(tc):
             "name": "__cpp_lib_stacktrace",
             "values": {"c++23": 202011},
             "headers": ["stacktrace"],
-            "unimplemented": True,
+            "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE",
+            "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_STACKTRACE",
         },
         {
             "name": "__cpp_lib_starts_ends_with",
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index d06271a7908cc..410ffce8795a2 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -170,7 +170,6 @@ def __hash__(self) -> int:
     "linalg",
     "rcu",
     "spanstream",
-    "stacktrace",
     "stdfloat",
     "text_encoding",
 ]))
@@ -198,6 +197,7 @@ def __hash__(self) -> int:
     "print": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14, c++17, c++20, availability-fp_to_chars-missing", # TODO PRINT investigate
     "semaphore": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
     "shared_mutex": "// UNSUPPORTED: no-threads, c++03, c++11",
+    "stacktrace": "// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20",
     "stdatomic.h": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17, c++20",
     "stop_token": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
     "thread": "// UNSUPPORTED: no-threads, c++03",
@@ -244,6 +244,7 @@ def __hash__(self) -> int:
     "regex": ["compare", "initializer_list"],
     "set": ["compare", "initializer_list"],
     "stack": ["compare", "initializer_list"],
+    "stacktrace": ["compare"],
     "string_view": ["compare"],
     "string": ["compare", "initializer_list"],
     "syncstream": ["ostream"],
diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
index 7d6e78de343c5..55e1d258f0f6f 100644
--- a/libcxx/utils/libcxx/test/features.py
+++ b/libcxx/utils/libcxx/test/features.py
@@ -889,4 +889,12 @@ def check_gdb(cfg):
             cfg.available_features,
         ),
     ),
+    # Tests that require std::[basic_]stacktrace in the built library
+    Feature(
+        name="availability-stacktrace-missing",
+        when=lambda cfg: BooleanExpression.evaluate(
+            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-22)",
+            cfg.available_features,
+        ),
+    ),
 ]

>From e64e551789db21d091711c038de77f63521d8b0e Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Fri, 24 Oct 2025 09:00:04 -0400
Subject: [PATCH 2/9] Support Windows 32-bit (and other smaller fixes)

---
 libcxx/include/__stacktrace/basic_stacktrace.h | 6 +++---
 libcxx/include/__stacktrace/stacktrace_entry.h | 6 +++---
 libcxx/src/stacktrace/impl_windows.cpp         | 6 +++++-
 3 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index 291ca672631b1..94ba81d43b9ca 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_STACKTRACE_BASIC
-#define _LIBCPP_STACKTRACE_BASIC
+#ifndef _LIBCPP_BASIC_STACKTRACE_H
+#define _LIBCPP_BASIC_STACKTRACE_H
 
 #include <__config>
 
@@ -339,4 +339,4 @@ _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP_STACKTRACE_BASIC
+#endif // _LIBCPP_BASIC_STACKTRACE_H
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index 48daf14ccccc6..1168195a56de0 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_STACKTRACE_ENTRY
-#define _LIBCPP_STACKTRACE_ENTRY
+#ifndef _LIBCPP_STACKTRACE_ENTRY_H
+#define _LIBCPP_STACKTRACE_ENTRY_H
 
 #include <__config>
 
@@ -208,4 +208,4 @@ _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP_STACKTRACE_ENTRY
+#endif // _LIBCPP_STACKTRACE_ENTRY_H
diff --git a/libcxx/src/stacktrace/impl_windows.cpp b/libcxx/src/stacktrace/impl_windows.cpp
index c739f3731c175..9cb9b3508a3a5 100644
--- a/libcxx/src/stacktrace/impl_windows.cpp
+++ b/libcxx/src/stacktrace/impl_windows.cpp
@@ -171,8 +171,12 @@ base::current_impl(size_t skip, size_t max_depth) {
   frame.AddrPC.Offset    = ccx.Pc;
   frame.AddrStack.Offset = ccx.Sp;
   frame.AddrFrame.Offset = ccx.Fp;
+#elif defined(_M_IX86)
+  frame.AddrPC.Offset    = ccx.Eip;
+  frame.AddrStack.Offset = ccx.Esp;
+  frame.AddrFrame.Offset = ccx.Ebp;
 #else
-# warning stacktrace requires x86-64 or ARM64; returned stacktraces will be empty
+# warning unrecognized architecture; returned stacktraces will be empty
   return;
 #endif
 

>From 93f24b42f08e69365644b55f2ef002160ca62469 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Fri, 24 Oct 2025 00:13:27 -0400
Subject: [PATCH 3/9] Formatting, fix generated files, etc.

---
 ...bcxxabi.v1.stable.exceptions.nonew.abilist | 98 ++++++++++++++++---
 libcxx/test/support/test_macros.h             | 16 +--
 2 files changed, 92 insertions(+), 22 deletions(-)

diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index f6d454ccb2f23..56fcfc55d7fa2 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -35,6 +35,7 @@
 {'is_defined': False, 'name': '_ZTVSt13runtime_error', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': False, 'name': '_ZTVSt14overflow_error', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': False, 'name': '_ZTVSt16invalid_argument', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': False, 'name': '_ZTVSt9exception', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': False, 'name': '_ZdaPv', 'type': 'FUNC'}
 {'is_defined': False, 'name': '_ZdaPvSt11align_val_t', 'type': 'FUNC'}
 {'is_defined': False, 'name': '_ZdlPv', 'type': 'FUNC'}
@@ -59,6 +60,7 @@
 {'is_defined': False, 'name': '__cxa_rethrow', 'type': 'FUNC'}
 {'is_defined': False, 'name': '__cxa_rethrow_primary_exception', 'type': 'FUNC'}
 {'is_defined': False, 'name': '__cxa_throw', 'type': 'FUNC'}
+{'is_defined': False, 'name': '__cxa_throw_bad_array_new_length', 'type': 'FUNC'}
 {'is_defined': False, 'name': '__cxa_uncaught_exceptions', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt12bad_any_cast4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt12experimental15fundamentals_v112bad_any_cast4whatEv', 'type': 'FUNC'}
@@ -597,6 +599,22 @@
 {'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__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPaLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPcLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPdLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPeLb0EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPfLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPhLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPiLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPjLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPlLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPmLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPsLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPtLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPwLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPxLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPyLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyERNS_6__lessIvvEEPNS_12__stacktrace5imageELb0EEEvT1_S8_T0_NS_15iterator_traitsIS8_E15difference_typeEb', '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'}
@@ -627,6 +645,7 @@
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace12unwind_addrsERNS0_4baseEmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base12current_implEmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
@@ -781,6 +800,8 @@
 {'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__MIN_BLOCK_2E', 'size': 69, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__113__POW10_SPLITE', 'size': 29376, 'type': 'OBJECT'}
 {'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'}
@@ -962,6 +983,7 @@
 {'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__114__POW10_OFFSETE', 'size': 128, 'type': 'OBJECT'}
 {'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': 33, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__114__num_put_base12__format_intEPcPKcbj', 'type': 'FUNC'}
@@ -1014,10 +1036,12 @@
 {'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__114error_categoryC1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114error_categoryC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114error_categoryD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114error_categoryD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114error_categoryD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__115__POW10_SPLIT_2E', 'size': 75192, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__115__get_classnameEPKcb', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__115__thread_struct25notify_all_at_thread_exitEPNS_18condition_variableEPNS_5mutexE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__115__thread_struct27__make_ready_at_thread_exitEPNS_17__assoc_sub_stateE', 'type': 'FUNC'}
@@ -1137,14 +1161,19 @@
 {'is_defined': True, 'name': '_ZNSt3__115recursive_mutexD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__115recursive_mutexD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__115system_categoryEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__116__POW10_OFFSET_2E', 'size': 138, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__116__check_groupingERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjS8_Rj', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__116__d2s_buffered_nEPcS0_dNS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__116__f2s_buffered_nEPcS0_fNS_12chars_formatE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm32EED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm32EED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm32EED2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__116__parse_exponentEPKcmmc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116generic_categoryEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__117__append_n_digitsEjjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state10__sub_waitERNS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state12__make_readyEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state13set_exceptionESt13exception_ptr', 'type': 'FUNC'}
@@ -1155,6 +1184,7 @@
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state4waitEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state9__executeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state9set_valueEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__117__merge_exponentsElli', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED2Ev', 'type': 'FUNC'}
@@ -1169,11 +1199,19 @@
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__FLOAT_POW5_SPLITE', 'size': 376, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__118__calculate_resultIdmEENS_19__from_chars_resultIT_EET0_ibS3_', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__calculate_resultIfjEENS_19__from_chars_resultIT_EET0_ibS3_', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__d2exp_buffered_nEPcS0_dj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC2EPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwE4initERKNS_5ctypeIwEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwE9__analyzeEcRKNS_5ctypeIwEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC1EPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC2EPKc', 'type': 'FUNC'}
@@ -1193,6 +1231,7 @@
 {'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutex8try_lockEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutexC1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutexC2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__119__DOUBLE_POW5_SPLITE', 'size': 5216, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__119__is_posix_terminalEP8_IO_FILE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__119__shared_mutex_base11lock_sharedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__119__shared_mutex_base13unlock_sharedEv', 'type': 'FUNC'}
@@ -1213,10 +1252,15 @@
 {'is_defined': True, 'name': '_ZNSt3__119__thread_local_dataEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__120__append_nine_digitsEjPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__120__d2fixed_buffered_nEPcS0_dj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__120__get_collation_nameEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEEi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__120__libcpp_atomic_waitEPVKvi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__120__throw_system_errorEiPKc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEE11set_pointerEPS1_', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEEC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEEC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__121__throw_runtime_errorEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutex4lockEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutex6unlockEv', 'type': 'FUNC'}
@@ -1225,19 +1269,43 @@
 {'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__122_Floating_to_chars_ryuIdEENS_15to_chars_resultEPcS2_T_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__122_Floating_to_chars_ryuIfEENS_15to_chars_resultEPcS2_T_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__122__FLOAT_POW5_INV_SPLITE', 'size': 248, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__122__libcpp_verbose_abortEPKcz', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__123__DOUBLE_POW5_INV_SPLITE', 'size': 4672, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_allEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_allEPVKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_oneEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_oneEPVKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE16_Special_X_tableE', 'size': 1560, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE17_Ordinary_X_tableE', 'size': 2512, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE6_Max_PE', 'size': 4, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE16_Special_X_tableE', 'size': 252, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE17_Ordinary_X_tableE', 'size': 176, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE6_Max_PE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_hexIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_hexIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_infIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_infIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_nanIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_nanIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__parse_fractional_hex_constantIjEENS_28__fractional_constant_resultIT_EEPKcmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__parse_fractional_hex_constantImEENS_28__fractional_constant_resultIT_EEPKcmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__132__from_chars_floating_point_implIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__132__from_chars_floating_point_implIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__135__from_chars_floating_point_decimalIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatES5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__135__from_chars_floating_point_decimalIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatES5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__135__parse_fractional_decimal_constantIjEENS_28__fractional_constant_resultIT_EEPKcll', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__135__parse_fractional_decimal_constantImEENS_28__fractional_constant_resultIT_EEPKcll', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13cinE', 'size': 280, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD1Ev', 'type': 'FUNC'}
@@ -1246,6 +1314,8 @@
 {'is_defined': True, 'name': '_ZNSt3__13pmr20get_default_resourceEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr20null_memory_resourceEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr20set_default_resourceEPNS0_15memory_resourceE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__13pmr25__try_allocate_from_chunkILb0ENS0_25monotonic_buffer_resource14__chunk_footerEEEPvRT0_mm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__13pmr25__try_allocate_from_chunkILb1ENS0_25monotonic_buffer_resource20__initial_descriptorEEEPvRT0_mm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr25monotonic_buffer_resource11do_allocateEmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr28unsynchronized_pool_resource11do_allocateEmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__do_allocateEPNS0_15memory_resourceEmm', 'type': 'FUNC'}
@@ -1302,6 +1372,7 @@
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem4path8iterator11__decrementEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem4path8iterator11__incrementEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem6__copyERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem6detail14FileDescriptor14refresh_statusERNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem7__spaceERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE', 'type': 'FUNC'}
@@ -1346,6 +1417,13 @@
 {'is_defined': True, 'name': '_ZNSt3__15wclogE', 'size': 264, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 264, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa10__pow10_32E', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa10__pow10_64E', 'size': 160, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa12__base_2_lutE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa12__base_8_lutE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa13__base_16_lutE', 'size': 512, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa16_Charconv_digitsE', 'size': 36, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa16__digits_base_10E', 'size': 200, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
@@ -1558,6 +1636,10 @@
 {'is_defined': True, 'name': '_ZNSt3__18valarrayImEC2Em', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__18valarrayImED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__18valarrayImED2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitC2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitD1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19__num_getIcE17__stage2_int_loopEciPcRS2_RjcRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_S2_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19__num_getIcE17__stage2_int_prepERNS_8ios_baseEPcRc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19__num_getIcE19__stage2_float_loopEcRbRcPcRS4_ccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjS4_', 'type': 'FUNC'}
@@ -1603,20 +1685,6 @@
 {'is_defined': True, 'name': '_ZSt18uncaught_exceptionv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZSt19uncaught_exceptionsv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZSt7nothrow', 'size': 1, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__110istrstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__110ostrstreamE0_NS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE16_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE0_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE', 'size': 120, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE0_NS_14basic_iostreamIcNS_11char_traitsIcEEEE', 'size': 120, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE16_NS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt12experimental15fundamentals_v112bad_any_castE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt12experimental19bad_optional_accessE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__110__time_getE', 'size': 16, 'type': 'OBJECT'}
@@ -1967,6 +2035,8 @@
 {'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IDiEE', 'size': 96, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IDsEE', 'size': 96, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IwEE', 'size': 96, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__120__time_get_c_storageIcEE', 'size': 72, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__120__time_get_c_storageIwEE', 'size': 72, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__13pmr15memory_resourceE', 'size': 56, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__13pmr25monotonic_buffer_resourceE', 'size': 56, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__13pmr26synchronized_pool_resourceE', 'size': 56, 'type': 'OBJECT'}
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 5f00c1f994662..2699d5371a7fb 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -363,19 +363,19 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 // Disables tail-call optimization for "outbound" calls
 // performed in the function annotated with this attribute.
 #if __has_cpp_attribute(_Clang::__disable_tail_calls__)
-#define TEST_NO_TAIL_CALLS_OUT [[_Clang::__disable_tail_calls__]]
+#  define TEST_NO_TAIL_CALLS_OUT [[_Clang::__disable_tail_calls__]]
 #elif __has_cpp_attribute(__gnu__::__optimize__)
-#define TEST_NO_TAIL_CALLS_OUT [[__gnu__::__optimize__("no-optimize-sibling-calls")]]
+#  define TEST_NO_TAIL_CALLS_OUT [[__gnu__::__optimize__("no-optimize-sibling-calls")]]
 #else
-#define TEST_NO_TAIL_CALLS_OUT
+#  define TEST_NO_TAIL_CALLS_OUT
 #endif
 
 // Disables tail-call optimization for "inbound" calls -- that is,
 // calls from some other function calling the one having this attribute.
 #if __has_cpp_attribute(_Clang::__not_tail_called__)
-#define TEST_NO_TAIL_CALLS_IN [[_Clang::__not_tail_called__]]
+#  define TEST_NO_TAIL_CALLS_IN [[_Clang::__not_tail_called__]]
 #else
-#define TEST_NO_TAIL_CALLS_IN
+#  define TEST_NO_TAIL_CALLS_IN
 #endif
 
 #ifdef _WIN32
@@ -395,8 +395,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
-#define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
-#define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
+#  define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
+#  define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) assert(__VA_ARGS__)
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 1
@@ -417,7 +417,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 // the end user executable, and these fallbacks work even in DLL configurations.
 // In MinGW configurations when built as a DLL, and on zOS, these fallbacks
 // don't work though.
-#define ASSERT_WITH_OPERATOR_NEW_FALLBACKS(...) ((void)(__VA_ARGS__))
+#  define ASSERT_WITH_OPERATOR_NEW_FALLBACKS(...) ((void)(__VA_ARGS__))
 #else
 #define ASSERT_WITH_OPERATOR_NEW_FALLBACKS(...) assert(__VA_ARGS__)
 #endif

>From a3f8c5d7e102aa885d3718697dfc03aab5525867 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sat, 25 Oct 2025 16:28:56 -0400
Subject: [PATCH 4/9] De-uglify issue links

---
 libcxx/docs/ReleaseNotes/22.rst | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index e1b252b696fef..b628a10350036 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -46,8 +46,8 @@ Implemented Papers
 - P2835R7: Expose ``std::atomic_ref``'s object address (`Github <https://llvm.org/PR118377>`__)
 - P2944R3: Comparisons for ``reference_wrapper`` (`Github <https://llvm.org/PR105424>`__)
 - P3168R2: Give ``std::optional`` Range Support (`Github <https://llvm.org/PR105430>`__)
-- P0881R7: A Proposal to add stacktrace library (`Github <https://github.com/llvm/llvm-project/issues/105131>`__)
-- P2301R1: Add a `pmr` alias for `std::stacktrace` (`Github <https://github.com/llvm/llvm-project/issues/105167>`__)
+- P0881R7: A Proposal to add stacktrace library (`Github <https://llvm.org/PR105131>`__)
+- P2301R1: Add a `pmr` alias for `std::stacktrace` (`Github <https://llvm.org/PR105167>`__)
 
 Improvements and New Features
 -----------------------------

>From 82d49388a265bfe6ef22bf5f9eb7e3672ba67f62 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sat, 25 Oct 2025 16:20:49 -0400
Subject: [PATCH 5/9] Update ABI list, WTF edition.  Hopefully fixed

---
 ...bcxxabi.v1.stable.exceptions.nonew.abilist | 98 +++----------------
 1 file changed, 14 insertions(+), 84 deletions(-)

diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 56fcfc55d7fa2..f6d454ccb2f23 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -35,7 +35,6 @@
 {'is_defined': False, 'name': '_ZTVSt13runtime_error', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': False, 'name': '_ZTVSt14overflow_error', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': False, 'name': '_ZTVSt16invalid_argument', 'size': 0, 'type': 'OBJECT'}
-{'is_defined': False, 'name': '_ZTVSt9exception', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': False, 'name': '_ZdaPv', 'type': 'FUNC'}
 {'is_defined': False, 'name': '_ZdaPvSt11align_val_t', 'type': 'FUNC'}
 {'is_defined': False, 'name': '_ZdlPv', 'type': 'FUNC'}
@@ -60,7 +59,6 @@
 {'is_defined': False, 'name': '__cxa_rethrow', 'type': 'FUNC'}
 {'is_defined': False, 'name': '__cxa_rethrow_primary_exception', 'type': 'FUNC'}
 {'is_defined': False, 'name': '__cxa_throw', 'type': 'FUNC'}
-{'is_defined': False, 'name': '__cxa_throw_bad_array_new_length', 'type': 'FUNC'}
 {'is_defined': False, 'name': '__cxa_uncaught_exceptions', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt12bad_any_cast4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt12experimental15fundamentals_v112bad_any_cast4whatEv', 'type': 'FUNC'}
@@ -599,22 +597,6 @@
 {'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__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPaLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPcLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPdLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPeLb0EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPfLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPhLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPiLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPjLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPlLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPmLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPsLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPtLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPwLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPxLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPyLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyERNS_6__lessIvvEEPNS_12__stacktrace5imageELb0EEEvT1_S8_T0_NS_15iterator_traitsIS8_E15difference_typeEb', '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'}
@@ -645,7 +627,6 @@
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace12unwind_addrsERNS0_4baseEmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base12current_implEmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
@@ -800,8 +781,6 @@
 {'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__MIN_BLOCK_2E', 'size': 69, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__113__POW10_SPLITE', 'size': 29376, 'type': 'OBJECT'}
 {'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'}
@@ -983,7 +962,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__114__POW10_OFFSETE', 'size': 128, 'type': 'OBJECT'}
 {'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': 33, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__114__num_put_base12__format_intEPcPKcbj', 'type': 'FUNC'}
@@ -1036,12 +1014,10 @@
 {'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__114error_categoryC1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114error_categoryC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114error_categoryD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114error_categoryD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114error_categoryD2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__115__POW10_SPLIT_2E', 'size': 75192, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__115__get_classnameEPKcb', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__115__thread_struct25notify_all_at_thread_exitEPNS_18condition_variableEPNS_5mutexE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__115__thread_struct27__make_ready_at_thread_exitEPNS_17__assoc_sub_stateE', 'type': 'FUNC'}
@@ -1161,19 +1137,14 @@
 {'is_defined': True, 'name': '_ZNSt3__115recursive_mutexD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__115recursive_mutexD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__115system_categoryEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__116__POW10_OFFSET_2E', 'size': 138, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__116__check_groupingERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjS8_Rj', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__116__d2s_buffered_nEPcS0_dNS_12chars_formatE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__116__f2s_buffered_nEPcS0_fNS_12chars_formatE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm32EED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm32EED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm32EED2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__116__parse_exponentEPKcmmc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116generic_categoryEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__117__append_n_digitsEjjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state10__sub_waitERNS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state12__make_readyEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state13set_exceptionESt13exception_ptr', 'type': 'FUNC'}
@@ -1184,7 +1155,6 @@
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state4waitEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state9__executeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state9set_valueEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__117__merge_exponentsElli', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED2Ev', 'type': 'FUNC'}
@@ -1199,19 +1169,11 @@
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__FLOAT_POW5_SPLITE', 'size': 376, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__118__calculate_resultIdmEENS_19__from_chars_resultIT_EET0_ibS3_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__calculate_resultIfjEENS_19__from_chars_resultIT_EET0_ibS3_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__d2exp_buffered_nEPcS0_dj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC2EPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwE4initERKNS_5ctypeIwEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwE9__analyzeEcRKNS_5ctypeIwEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC1EPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC2EPKc', 'type': 'FUNC'}
@@ -1231,7 +1193,6 @@
 {'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutex8try_lockEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutexC1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutexC2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__119__DOUBLE_POW5_SPLITE', 'size': 5216, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__119__is_posix_terminalEP8_IO_FILE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__119__shared_mutex_base11lock_sharedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__119__shared_mutex_base13unlock_sharedEv', 'type': 'FUNC'}
@@ -1252,15 +1213,10 @@
 {'is_defined': True, 'name': '_ZNSt3__119__thread_local_dataEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__120__append_nine_digitsEjPc', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__120__d2fixed_buffered_nEPcS0_dj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__120__get_collation_nameEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEEi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__120__libcpp_atomic_waitEPVKvi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__120__throw_system_errorEiPKc', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEE11set_pointerEPS1_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEEC1Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEEC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__121__throw_runtime_errorEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutex4lockEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutex6unlockEv', 'type': 'FUNC'}
@@ -1269,43 +1225,19 @@
 {'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexD2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__122_Floating_to_chars_ryuIdEENS_15to_chars_resultEPcS2_T_NS_12chars_formatE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__122_Floating_to_chars_ryuIfEENS_15to_chars_resultEPcS2_T_NS_12chars_formatE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__122__FLOAT_POW5_INV_SPLITE', 'size': 248, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__122__libcpp_verbose_abortEPKcz', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__123__DOUBLE_POW5_INV_SPLITE', 'size': 4672, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_allEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_allEPVKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_oneEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_oneEPVKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE16_Special_X_tableE', 'size': 1560, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE17_Ordinary_X_tableE', 'size': 2512, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE6_Max_PE', 'size': 4, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE16_Special_X_tableE', 'size': 252, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE17_Ordinary_X_tableE', 'size': 176, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE6_Max_PE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_hexIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_hexIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_infIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_infIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_nanIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_nanIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__parse_fractional_hex_constantIjEENS_28__fractional_constant_resultIT_EEPKcmm', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__parse_fractional_hex_constantImEENS_28__fractional_constant_resultIT_EEPKcmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__132__from_chars_floating_point_implIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__132__from_chars_floating_point_implIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__135__from_chars_floating_point_decimalIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatES5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__135__from_chars_floating_point_decimalIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatES5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__135__parse_fractional_decimal_constantIjEENS_28__fractional_constant_resultIT_EEPKcll', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__135__parse_fractional_decimal_constantImEENS_28__fractional_constant_resultIT_EEPKcll', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13cinE', 'size': 280, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD1Ev', 'type': 'FUNC'}
@@ -1314,8 +1246,6 @@
 {'is_defined': True, 'name': '_ZNSt3__13pmr20get_default_resourceEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr20null_memory_resourceEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr20set_default_resourceEPNS0_15memory_resourceE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__13pmr25__try_allocate_from_chunkILb0ENS0_25monotonic_buffer_resource14__chunk_footerEEEPvRT0_mm', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__13pmr25__try_allocate_from_chunkILb1ENS0_25monotonic_buffer_resource20__initial_descriptorEEEPvRT0_mm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr25monotonic_buffer_resource11do_allocateEmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr28unsynchronized_pool_resource11do_allocateEmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__do_allocateEPNS0_15memory_resourceEmm', 'type': 'FUNC'}
@@ -1372,7 +1302,6 @@
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem4path8iterator11__decrementEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem4path8iterator11__incrementEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem6__copyERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem6detail14FileDescriptor14refresh_statusERNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem7__spaceERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE', 'type': 'FUNC'}
@@ -1417,13 +1346,6 @@
 {'is_defined': True, 'name': '_ZNSt3__15wclogE', 'size': 264, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 264, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa10__pow10_32E', 'size': 40, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa10__pow10_64E', 'size': 160, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa12__base_2_lutE', 'size': 64, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa12__base_8_lutE', 'size': 128, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa13__base_16_lutE', 'size': 512, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa16_Charconv_digitsE', 'size': 36, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa16__digits_base_10E', 'size': 200, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
@@ -1636,10 +1558,6 @@
 {'is_defined': True, 'name': '_ZNSt3__18valarrayImEC2Em', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__18valarrayImED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__18valarrayImED2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitC1Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitC2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitD1Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19__num_getIcE17__stage2_int_loopEciPcRS2_RjcRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_S2_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19__num_getIcE17__stage2_int_prepERNS_8ios_baseEPcRc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19__num_getIcE19__stage2_float_loopEcRbRcPcRS4_ccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjS4_', 'type': 'FUNC'}
@@ -1685,6 +1603,20 @@
 {'is_defined': True, 'name': '_ZSt18uncaught_exceptionv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZSt19uncaught_exceptionsv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZSt7nothrow', 'size': 1, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__110istrstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__110ostrstreamE0_NS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE16_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE0_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE', 'size': 120, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE0_NS_14basic_iostreamIcNS_11char_traitsIcEEEE', 'size': 120, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE16_NS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt12experimental15fundamentals_v112bad_any_castE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt12experimental19bad_optional_accessE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__110__time_getE', 'size': 16, 'type': 'OBJECT'}
@@ -2035,8 +1967,6 @@
 {'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IDiEE', 'size': 96, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IDsEE', 'size': 96, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IwEE', 'size': 96, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTVNSt3__120__time_get_c_storageIcEE', 'size': 72, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTVNSt3__120__time_get_c_storageIwEE', 'size': 72, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__13pmr15memory_resourceE', 'size': 56, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__13pmr25monotonic_buffer_resourceE', 'size': 56, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__13pmr26synchronized_pool_resourceE', 'size': 56, 'type': 'OBJECT'}

>From 3cd7feed3203fcf315a8547aa0c6fc6a75834a41 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sun, 26 Oct 2025 11:51:56 -0400
Subject: [PATCH 6/9] Impl stack-height-sensitive code in headers, remove some
 macros

---
 libcxx/include/__config                       |  27 -----
 .../include/__stacktrace/basic_stacktrace.h   |  89 +++++++++++++--
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   4 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   4 +-
 libcxx/src/CMakeLists.txt                     |   1 -
 libcxx/src/stacktrace/images.h                |  10 +-
 libcxx/src/stacktrace/impl_generic.cpp        |  56 +--------
 libcxx/src/stacktrace/impl_windows.cpp        |   4 +-
 libcxx/src/stacktrace/unwinding.h             | 108 ------------------
 .../assert.current.no_overflow.pass.cpp       |   1 +
 .../stacktrace/basic.cons/copy.pass.cpp       |   3 +-
 .../basic.cons/ctor_no_args.pass.cpp          |   1 +
 .../basic.cons/ctor_with_alloc.pass.cpp       |   1 +
 .../basic.cons/current_no_args.pass.cpp       |  10 +-
 .../basic.cons/current_skip.pass.cpp          |   5 +-
 .../basic.cons/current_skip_depth.pass.cpp    |   5 +-
 .../stacktrace/basic.cons/move.pass.cpp       |   3 +-
 .../stacktrace/basic.obs/at.pass.cpp          |  10 +-
 .../stacktrace/basic.obs/begin_end.pass.cpp   |  10 +-
 .../stacktrace/basic.obs/cbegin_cend.pass.cpp |  10 +-
 .../basic.obs/crbegin_crend.pass.cpp          |  10 +-
 .../stacktrace/basic.obs/empty.pass.cpp       |  10 +-
 .../stacktrace/basic.obs/max_size.pass.cpp    |  10 +-
 .../basic.obs/operator_index.pass.cpp         |  10 +-
 .../stacktrace/basic.obs/rbegin_rend.pass.cpp |  10 +-
 .../stacktrace/basic.obs/size.pass.cpp        |  10 +-
 .../entry.cons/copy_assign.pass.cpp           |   4 +-
 .../entry.cons/copy_construct.pass.cpp        |   4 +-
 .../stacktrace/only_uses_allocator.pass.cpp   |   3 +-
 libcxx/test/support/test_macros.h             |   2 +
 30 files changed, 182 insertions(+), 253 deletions(-)
 delete mode 100644 libcxx/src/stacktrace/unwinding.h

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 4d32f1fba03c7..b4c081dcdff1b 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -965,33 +965,6 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_NOINLINE
 #  endif
 
-// Some functions, e.g. std::stacktrace::current, need to avoid being
-// tail-called by (and tail-calling other) functions, for proper enumeration of
-// call-stack frames.
-// clang-format off
-
-// Disables tail-call optimization for "outbound" calls
-// performed in the function annotated with this attribute.
-#  if __has_cpp_attribute(_Clang::__disable_tail_calls__)
-#    define _LIBCPP_NO_TAIL_CALLS_OUT [[_Clang::__disable_tail_calls__]]
-#  elif __has_cpp_attribute(__gnu__::__optimize__)
-#    define _LIBCPP_NO_TAIL_CALLS_OUT [[__gnu__::__optimize__("no-optimize-sibling-calls")]]
-#  else
-#    define _LIBCPP_NO_TAIL_CALLS_OUT
-#  endif
-
-// Disables tail-call optimization for "inbound" calls -- that is,
-// calls from some other function calling the one having this attribute.
-#  if __has_cpp_attribute(_Clang::__not_tail_called__)
-#    define _LIBCPP_NO_TAIL_CALLS_IN [[_Clang::__not_tail_called__]]
-#  else
-#    define _LIBCPP_NO_TAIL_CALLS_IN
-#  endif
-
-// Disable TCO for calls into, and out from, the annotated function.
-#  define _LIBCPP_NO_TAIL_CALLS _LIBCPP_NO_TAIL_CALLS_IN _LIBCPP_NO_TAIL_CALLS_OUT
-// clang-format on
-
 // We often repeat things just for handling wide characters in the library.
 // When wide characters are disabled, it can be useful to have a quick way of
 // disabling it without having to resort to #if-#endif, which has a larger
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index 94ba81d43b9ca..3e110e23056ca 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -40,6 +40,10 @@ _LIBCPP_PUSH_MACROS
 #  include <__fwd/ostream.h>
 #endif // _LIBCPP_HAS_LOCALIZATION
 
+#if !defined(_WIN32)
+#  include <unwind.h>
+#endif
+
 #if _LIBCPP_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
 
 #  include <__stacktrace/stacktrace_entry.h>
@@ -76,11 +80,30 @@ struct base {
         __entry_iters_(__entry_iters),
         __entry_append_(__entry_append) {}
 
-  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void current_impl(size_t __skip, size_t __max_depth);
+#  ifndef _WIN32
+  // Use <unwind.h> or <libunwind.h> to collect addresses in the stack.
+  // Defined in this header so it can be reliably force-inlined.
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE void unwind_addrs(size_t __skip, size_t __depth);
+#  endif
+
+  // On Windows, there are DLLs which take care of all this (dbghelp, psapi), with
+  // zero overlap with any other OS, so it's in its own file, impl_windows.cpp.
+  // For all other platforms we implement these "find" methods.
+
+  // Resolve instruction addresses to their respective images, dealing with possible ASLR.
+  _LIBCPP_EXPORTED_FROM_ABI void find_images();
 
-  _LIBCPP_HIDE_FROM_ABI void find_images();
-  _LIBCPP_HIDE_FROM_ABI void find_symbols();
-  _LIBCPP_HIDE_FROM_ABI void find_source_locs();
+  // Resolve addresses to symbols if possible.
+  _LIBCPP_EXPORTED_FROM_ABI void find_symbols();
+
+  // Resolve addresses to source locations if possible.
+  _LIBCPP_EXPORTED_FROM_ABI void find_source_locs();
+
+  _LIBCPP_HIDE_FROM_ABI void finish_stacktrace() {
+    find_images();
+    find_symbols();
+    find_source_locs();
+  }
 
   _LIBCPP_EXPORTED_FROM_ABI ostream& write_to(ostream& __os) const;
   _LIBCPP_EXPORTED_FROM_ABI string to_string() const;
@@ -147,7 +170,8 @@ class basic_stacktrace : private __stacktrace::base {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
         __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
     basic_stacktrace __ret{__caller_alloc};
-    __ret.current_impl(__skip, __max_depth);
+    __ret.unwind_addrs(__skip, __max_depth);
+    __ret.finish_stacktrace();
     return __ret;
   }
 
@@ -157,7 +181,8 @@ class basic_stacktrace : private __stacktrace::base {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
         __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
     basic_stacktrace __ret{__caller_alloc};
-    __ret.current_impl(__skip, __max_depth);
+    __ret.unwind_addrs(__skip, __max_depth);
+    __ret.finish_stacktrace();
     return __ret;
   }
 
@@ -169,7 +194,8 @@ class basic_stacktrace : private __stacktrace::base {
         __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
     basic_stacktrace __ret{__caller_alloc};
     if (__max_depth) [[likely]] {
-      __ret.current_impl(__skip, __max_depth);
+      __ret.unwind_addrs(__skip, __max_depth);
+      __ret.finish_stacktrace();
     }
     return __ret;
   }
@@ -333,6 +359,55 @@ struct hash<basic_stacktrace<_Allocator>> {
   }
 };
 
+namespace __stacktrace {
+
+#  if defined(_WIN32)
+
+_LIBCPP_EXPORTED_FROM_ABI void base::windows_impl(size_t skip, size_t max_depth)
+
+#  else
+
+struct unwind_backtrace {
+  base& base_;
+  size_t skip_;
+  size_t maxDepth_;
+
+  _LIBCPP_HIDE_FROM_ABI _Unwind_Reason_Code callback(_Unwind_Context* __ucx) {
+    if (skip_) {
+      --skip_;
+      return _Unwind_Reason_Code::_URC_NO_REASON;
+    }
+    if (!maxDepth_) {
+      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+    }
+    --maxDepth_;
+    int __ip_before{0};
+    auto __ip = _Unwind_GetIPInfo(__ucx, &__ip_before);
+    if (!__ip) {
+      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+    }
+    auto& __entry = base_.__entry_append_();
+    auto& __eb    = (entry_base&)__entry;
+    __eb.__addr_  = (__ip_before ? __ip : __ip - 1);
+    return _Unwind_Reason_Code::_URC_NO_REASON;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static _Unwind_Reason_Code callback(_Unwind_Context* __cx, void* __self) {
+    return ((unwind_backtrace*)__self)->callback(__cx);
+  }
+};
+
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void base::unwind_addrs(size_t __skip, size_t __depth) {
+  if (!__depth) {
+    return;
+  }
+  unwind_backtrace __bt{*this, __skip, __depth};
+  _Unwind_Backtrace(unwind_backtrace::callback, &__bt);
+}
+
+#  endif // _WIN32
+
+} // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
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 0998827ebf058..ba952095d16a1 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
@@ -979,7 +979,9 @@
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base12current_implEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base11find_imagesEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base12find_symbolsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base16find_source_locsEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index f6d454ccb2f23..04c2115f96d7d 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -627,7 +627,9 @@
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base12current_implEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base11find_imagesEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base12find_symbolsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base16find_source_locsEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index aca66727d6442..6e930022ede4a 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -44,7 +44,6 @@ set(LIBCXX_SOURCES
   stacktrace/impl_generic.cpp
   stacktrace/impl_windows.cpp
   stacktrace/impl.cpp
-  stacktrace/unwinding.h
   stdexcept.cpp
   string.cpp
   support/runtime/exception_fallback.ipp
diff --git a/libcxx/src/stacktrace/images.h b/libcxx/src/stacktrace/images.h
index 5c7af94de31d6..476d684734878 100644
--- a/libcxx/src/stacktrace/images.h
+++ b/libcxx/src/stacktrace/images.h
@@ -19,11 +19,9 @@
 _LIBCPP_PUSH_MACROS
 #include <__undef_macros>
 
-#if _LIBCPP_STD_VER >= 23
-
-#  include <__stacktrace/stacktrace_entry.h>
-#  include <array>
-#  include <cstdint>
+#include <__stacktrace/stacktrace_entry.h>
+#include <array>
+#include <cstdint>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
@@ -112,8 +110,6 @@ struct images {
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_STD_VER >= 23
-
 _LIBCPP_POP_MACROS
 
 #endif // _LIBCPP_STACKTRACE_IMAGES_H
diff --git a/libcxx/src/stacktrace/impl_generic.cpp b/libcxx/src/stacktrace/impl_generic.cpp
index 315e0f21c30aa..ae3624def0816 100644
--- a/libcxx/src/stacktrace/impl_generic.cpp
+++ b/libcxx/src/stacktrace/impl_generic.cpp
@@ -6,65 +6,21 @@
 //
 //===----------------------------------------------------------------------===//
 
-/*
-"Generic" implementation for any platform that doesn't have its own special implementation.
-(Currently this means any platform other than Windows)
-*/
+// "Generic" implementation for any platform that doesn't have its own special implementation.
+// (Currently this means any platform other than Windows)
 
 #if !defined(_WIN32)
 
 #  include <__config>
+#  include <__stacktrace/basic_stacktrace.h>
 #  include <__stacktrace/stacktrace_entry.h>
 
 #  include "stacktrace/images.h"
-#  include "stacktrace/unwinding.h"
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
-base::current_impl(size_t skip, size_t max_depth) {
-  if (!max_depth) [[unlikely]] {
-    return;
-  }
-
-  /*
-  Build up the stacktrace entries and fill out their fields the best we can.
-  An `entry_base` structure looks more or less like:
-
-    __addr_     uintptr_t   Instruction address (of a call instruction, faulting insn, ...)
-    __desc_     string      "Description" which we'll say is synonymous with "symbol"
-    __file_     string      Source filename, or if that's not available, program/library path
-    __line_     int         Line number within the source file, or failing that, zero
-    __image_    image*      The image, loaded in by the OS, containing that address (program, library)
-
-  On Windows, there are DLLs which take care of all this (dbghelp, psapi), with essentially
-  zero overlap with any other OS, so it's in its own file (impl_windows).  Otherwise, i.e.
-  on non-Windows platforms, taking a stacktrace looks like:
-
-  0. Create the basic_stacktrace, its internal vector, using provided allocator.
-     (This was handled by the `current` member functions inside `basic_stacktrace`.)
-  1. Collect instruction addresses to build up the entries, observing skip and max_depth.
-     The unwinding library we have available to us should take care of walking the call stack
-     and finding addresses.
-  2. Resolve addresses into the program images (executable, libraries); normalize the addresses
-     from step 1, as they were seen in the wild, into program-image-relative addresses
-     (i.e. deal with ASLR)
-  3. Resolve adjusted addresses into their symbols; some environments provide this out of the box
-     (MacOS) and others make this less easy (Linux)
-  4. To get the source file and line number, we have to dig through debug information (DWARF format);
-     we might need the help of a library.
-  */
-
-  unwind_addrs(*this, skip + 1, max_depth);
-  if (__entry_iters_().size()) {
-    find_images();
-    find_symbols();
-    find_source_locs();
-  }
-}
-
-void base::find_images() {
+_LIBCPP_EXPORTED_FROM_ABI void base::find_images() {
   images images;
   size_t i = 0;
   for (auto& entry : __entry_iters_()) {
@@ -77,11 +33,11 @@ void base::find_images() {
   }
 }
 
-void base::find_symbols() {
+_LIBCPP_EXPORTED_FROM_ABI void base::find_symbols() {
   // TODO
 }
 
-void base::find_source_locs() {
+_LIBCPP_EXPORTED_FROM_ABI void base::find_source_locs() {
   // TODO
 }
 
diff --git a/libcxx/src/stacktrace/impl_windows.cpp b/libcxx/src/stacktrace/impl_windows.cpp
index 9cb9b3508a3a5..b31ecee8f1ee6 100644
--- a/libcxx/src/stacktrace/impl_windows.cpp
+++ b/libcxx/src/stacktrace/impl_windows.cpp
@@ -109,8 +109,8 @@ struct sym_init_scope {
 
 }  // namespace
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
-base::current_impl(size_t skip, size_t max_depth) {
+_LIBCPP_EXPORTED_FROM_ABI void
+base::windows_impl(size_t skip, size_t max_depth) {
   if (!max_depth) [[unlikely]] {
     return;
   }
diff --git a/libcxx/src/stacktrace/unwinding.h b/libcxx/src/stacktrace/unwinding.h
deleted file mode 100644
index d363ea1bc5a15..0000000000000
--- a/libcxx/src/stacktrace/unwinding.h
+++ /dev/null
@@ -1,108 +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 __LIBCPP_STACKTRACE_UNWIND_ADDRS_H
-#define __LIBCPP_STACKTRACE_UNWIND_ADDRS_H
-
-#include <__stacktrace/basic_stacktrace.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-void unwind_addrs(std::__stacktrace::base& base, size_t skip, size_t depth);
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#ifndef _WIN32
-
-#  if __has_include(<libunwind.h>)
-#    include <libunwind.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE inline void unwind_addrs(base& base, size_t skip, size_t depth) {
-  if (!depth) {
-    return;
-  }
-  unw_context_t cx;
-  unw_getcontext(&cx);
-  unw_cursor_t cur;
-  unw_init_local(&cur, &cx);
-  while (unw_step(&cur) > 0) {
-    if (skip && skip--) {
-      continue;
-    }
-    if (!depth--) {
-      break;
-    }
-    auto& entry = base.__entry_append_();
-    auto& eb    = (__stacktrace::entry_base&)entry;
-    unw_get_reg(&cur, UNW_REG_IP, &eb.__addr_);
-    if (!unw_is_signal_frame(&cur)) {
-      --eb.__addr_;
-    }
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#  elif __has_include(<unwind.h>)
-#    include <unwind.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct unwind_backtrace {
-  base& base_;
-  size_t skip_;
-  size_t maxDepth_;
-
-  _Unwind_Reason_Code callback(_Unwind_Context* ucx) {
-    if (skip_) {
-      --skip_;
-      return _Unwind_Reason_Code::_URC_NO_REASON;
-    }
-    if (!maxDepth_) {
-      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
-    }
-    --maxDepth_;
-    int ipBefore{0};
-    auto ip = _Unwind_GetIPInfo(ucx, &ipBefore);
-    if (!ip) {
-      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
-    }
-    auto& entry = base_.__entry_append_();
-    auto& eb    = (entry_base&)entry;
-    eb.__addr_  = (ipBefore ? ip : ip - 1);
-    return _Unwind_Reason_Code::_URC_NO_REASON;
-  }
-
-  static _Unwind_Reason_Code callback(_Unwind_Context* cx, void* self) {
-    return ((unwind_backtrace*)self)->callback(cx);
-  }
-};
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE inline void unwind_addrs(base& base, size_t skip, size_t depth) {
-  if (!depth) {
-    return;
-  }
-  unwind_backtrace bt{base, skip + 1, depth}; // skip this call as well
-  _Unwind_Backtrace(unwind_backtrace::callback, &bt);
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#  else
-#    error need <libunwind.h> or <unwind.h>
-#  endif
-
-#endif // _WIN32
-
-#endif // __LIBCPP_STACKTRACE_UNWIND_ADDRS_H
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp
index 50866ff5902e3..8d4efaac0442f 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp
@@ -21,6 +21,7 @@
 #include <stacktrace>
 
 #include "check_assertion.h"
+#include "test_macros.h"
 
 int main(int, char**) {
   TEST_LIBCPP_ASSERT_FAILURE(
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
index 2b3baffefeb50..94151583e4753 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
@@ -17,6 +17,7 @@
 #include <cassert>
 #include <stacktrace>
 
+#include "test_macros.h"
 #include "../test_allocs.h"
 
 void test_copy_construct() {
@@ -73,7 +74,7 @@ void test_copy_assign() {
   }
 }
 
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   test_copy_construct();
   test_copy_assign();
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
index 131e6ca2ad29b..129f8aa04bd80 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
@@ -16,6 +16,7 @@
 #include <stacktrace>
 #include <type_traits>
 
+#include "test_macros.h"
 #include "../test_allocs.h"
 
 void test_default_construct() {
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
index f28def7e70b6e..3d7a94165ad23 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
@@ -20,6 +20,7 @@
 #include <stacktrace>
 #include <type_traits>
 
+#include "test_macros.h"
 #include "../test_allocs.h"
 
 void test_construct_with_alloc() {
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
index bee38f57ffad0..750d6c695f3c7 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
@@ -21,23 +21,25 @@
 #include <cstdint>
 #include <stacktrace>
 
+#include "test_macros.h"
+
 uint32_t test1_line;
 uint32_t test2_line;
 uint32_t main_line;
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() {
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() {
   test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
   auto ret   = std::stacktrace::current();
   return ret;
 }
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() {
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() {
   test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
   auto ret   = test1();
   return ret;
 }
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current() {
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS void test_current() {
   main_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
   auto st   = test2();
 
@@ -59,7 +61,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current() {
   // assert(st[2].source_line() == main_line);
 }
 
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   static_assert(noexcept(std::stacktrace::current()));
   test_current();
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
index d0bf282d5fccd..c291d60d59c0c 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <stacktrace>
+#include "test_macros.h"
 
 /*
   Let t be a stacktrace as-if obtained via basic_stacktrace::current(alloc). Let n be t.size().
@@ -26,7 +27,7 @@
   t.begin() + min(n, skip), t.end(), and alloc, or an empty basic_stacktrace object if the
   initialization of frames_ failed.
 */
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip() {
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS void test_current_with_skip() {
   // Use default allocator for simplicity; alloc is covered above
   auto st_skip0 = std::stacktrace::current();
   assert(st_skip0.size() >= 2);
@@ -38,7 +39,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip() {
   assert(st_skip_many.empty());
 }
 
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   static_assert(noexcept(std::stacktrace::current(0)));
   test_current_with_skip();
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
index 300f95e8e97b3..a2cbee1f866b9 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
@@ -24,8 +24,9 @@
 #  include <cassert>
 #  include <iostream>
 #  include <stacktrace>
+#  include "test_macros.h"
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS void test_current_with_skip_depth() {
   // current stack is: [this function, main, (possibly something else, such as libc _start)]
   // so it's probably 3 functions deep -- but certainly at least 2 deep.
   std::stacktrace_entry entry;
@@ -46,7 +47,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
   assert(*it2 == entry);
 }
 
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   static_assert(noexcept(std::stacktrace::current(0, 0)));
   test_current_with_skip_depth();
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
index a659a515231f4..482842da3ddf8 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
@@ -24,6 +24,7 @@
 #include <stacktrace>
 #include <utility>
 
+#include "test_macros.h"
 #include "../test_allocs.h"
 
 void test_move_construct() {
@@ -81,7 +82,7 @@ void test_move_assign() {
   }
 }
 
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   test_move_construct();
   test_move_assign();
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
index 6084f3995b2c0..34562973bfc01 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
@@ -18,11 +18,13 @@
 #include <cassert>
 #include <stacktrace>
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+#include "test_macros.h"
 
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   auto st = test3();
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
index 3206879964134..64080931a2306 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
@@ -20,11 +20,13 @@
 #include <iterator>
 #include <stacktrace>
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+#include "test_macros.h"
 
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   std::stacktrace st;
   static_assert(noexcept(st.begin()));
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
index a3628e454b9bc..6381858846ecf 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
@@ -20,11 +20,13 @@
 #include <iterator>
 #include <stacktrace>
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+#include "test_macros.h"
 
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   std::stacktrace st;
   static_assert(noexcept(st.cbegin()));
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
index b211fb753a688..277e22c180b0e 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
@@ -20,11 +20,13 @@
 #include <iterator>
 #include <stacktrace>
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+#include "test_macros.h"
 
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   std::stacktrace st;
   static_assert(noexcept(st.crbegin()));
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
index 837e0bbf80acf..112e645f2c254 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
@@ -18,11 +18,13 @@
 #include <cassert>
 #include <stacktrace>
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+#include "test_macros.h"
 
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   std::stacktrace st;
   static_assert(noexcept(st.empty()));
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
index 3ba11885eff0b..bc31d63da9b21 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
@@ -19,11 +19,13 @@
 #include <stacktrace>
 #include <vector>
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+#include "test_macros.h"
 
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   std::stacktrace st;
   static_assert(noexcept(st.max_size()));
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
index bce77a7047f40..0a616bf2540b9 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
@@ -18,11 +18,13 @@
 #include <cassert>
 #include <stacktrace>
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+#include "test_macros.h"
 
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   auto st = test3();
   assert(st[0]);
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
index 471367e19436f..bfb7b907c5bfa 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
@@ -20,11 +20,13 @@
 #include <iterator>
 #include <stacktrace>
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+#include "test_macros.h"
 
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   std::stacktrace st;
   static_assert(noexcept(st.rbegin()));
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
index 03e003c6d2c0c..5afa6d957d653 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
@@ -18,11 +18,13 @@
 #include <cassert>
 #include <stacktrace>
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+#include "test_macros.h"
 
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   std::stacktrace st;
   static_assert(noexcept(st.size()));
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp
index 93198a4ac64a4..3065897428a11 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp
@@ -23,7 +23,9 @@ namespace std {
 #include <stacktrace>
 #include <type_traits>
 
-_LIBCPP_NO_TAIL_CALLS
+#include "test_macros.h"
+
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
index 822047c65d44b..2d6c63bb56903 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
@@ -23,7 +23,9 @@ namespace std {
 #include <stacktrace>
 #include <type_traits>
 
-_LIBCPP_NO_TAIL_CALLS
+#include "test_macros.h"
+
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp
index f733ae5786881..0d101f9d07a9a 100644
--- a/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp
@@ -18,6 +18,7 @@
 #include <stacktrace>
 
 #include "test_allocs.h"
+#include "test_macros.h"
 
 /*
  * This file includes tests which ensure any allocations performed by `basic_stacktrace`
@@ -90,7 +91,7 @@ struct test_alloc : std::allocator<T> {
   }
 };
 
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
 int main(int, char**) {
   (void)std::stacktrace::current();
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 2699d5371a7fb..06f3b9ee5e64a 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -378,6 +378,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #  define TEST_NO_TAIL_CALLS_IN
 #endif
 
+#define TEST_NO_TAIL_CALLS TEST_NO_TAIL_CALLS_IN TEST_NO_TAIL_CALLS_OUT
+
 #ifdef _WIN32
 #define TEST_NOT_WIN32(...) ((void)0)
 #else

>From b683fe0a8192f1e69b8f317ba6c3cb4ccd98cb2b Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sun, 26 Oct 2025 23:36:23 -0400
Subject: [PATCH 7/9] _Ugly_Names

---
 .../include/__stacktrace/basic_stacktrace.h   | 54 +++++++++---------
 .../include/__stacktrace/stacktrace_entry.h   | 56 +++++++++----------
 ...bcxxabi.v1.stable.exceptions.nonew.abilist | 18 +++---
 ...bcxxabi.v1.stable.exceptions.nonew.abilist | 18 +++---
 libcxx/src/stacktrace/images.cpp              | 12 ++--
 libcxx/src/stacktrace/images.h                | 22 ++++----
 libcxx/src/stacktrace/impl.cpp                | 10 ++--
 libcxx/src/stacktrace/impl_generic.cpp        |  8 +--
 libcxx/src/stacktrace/impl_windows.cpp        | 30 +++++-----
 9 files changed, 114 insertions(+), 114 deletions(-)

diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index 3e110e23056ca..1d343f95a6938 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -53,7 +53,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
 template <typename _Tp, typename _Bp = _Tp>
-struct iters {
+struct _Iters {
   _Tp* __data_{};
   size_t __size_{};
 
@@ -63,20 +63,20 @@ struct iters {
   _Bp* end() { return data() + size(); }
 };
 
-struct base {
+struct _Trace {
   constexpr static size_t __default_max_depth  = 64;
   constexpr static size_t __absolute_max_depth = 256;
 
-  str_alloc<char> __string_alloc_;
+  _Str_Alloc<char> __string_alloc_;
 
-  using _EntryIters _LIBCPP_NODEBUG = iters<stacktrace_entry, entry_base>;
+  using _EntryIters _LIBCPP_NODEBUG = _Iters<stacktrace_entry, _Entry>;
   function<_EntryIters()> __entry_iters_;
-  function<entry_base&()> __entry_append_;
+  function<_Entry&()> __entry_append_;
 
   template <class _Allocator>
   _LIBCPP_HIDE_FROM_ABI
-  base(_Allocator const& __alloc, function<_EntryIters()> __entry_iters, function<entry_base&()> __entry_append)
-      : __string_alloc_(std::move(str_alloc<char>::make(__alloc))),
+  _Trace(_Allocator const& __alloc, function<_EntryIters()> __entry_iters, function<_Entry&()> __entry_append)
+      : __string_alloc_(std::move(_Str_Alloc<char>::make(__alloc))),
         __entry_iters_(__entry_iters),
         __entry_append_(__entry_append) {}
 
@@ -108,7 +108,7 @@ struct base {
   _LIBCPP_EXPORTED_FROM_ABI ostream& write_to(ostream& __os) const;
   _LIBCPP_EXPORTED_FROM_ABI string to_string() const;
 
-  _LIBCPP_HIDE_FROM_ABI str __create_str() { return str(__string_alloc_); }
+  _LIBCPP_HIDE_FROM_ABI _Str __create_str() { return _Str(__string_alloc_); }
 };
 
 } // namespace __stacktrace
@@ -119,9 +119,9 @@ struct base {
 class stacktrace_entry;
 
 template <class _Allocator>
-class basic_stacktrace : private __stacktrace::base {
+class basic_stacktrace : private __stacktrace::_Trace {
   friend struct hash<basic_stacktrace<_Allocator>>;
-  friend struct __stacktrace::base;
+  friend struct __stacktrace::_Trace;
 
   using _ATraits _LIBCPP_NODEBUG            = allocator_traits<_Allocator>;
   constexpr static bool __kPropOnCopyAssign = _ATraits::propagate_on_container_copy_assignment::value;
@@ -134,15 +134,15 @@ class basic_stacktrace : private __stacktrace::base {
 
   vector<stacktrace_entry, _Allocator> __entries_;
   _LIBCPP_HIDE_FROM_ABI _EntryIters entry_iters() { return {__entries_.data(), __entries_.size()}; }
-  _LIBCPP_HIDE_FROM_ABI __stacktrace::entry_base& entry_append() {
-    return (__stacktrace::entry_base&)__entries_.emplace_back();
+  _LIBCPP_HIDE_FROM_ABI __stacktrace::_Entry& entry_append() {
+    return (__stacktrace::_Entry&)__entries_.emplace_back();
   }
 
   _LIBCPP_HIDE_FROM_ABI auto entry_iters_fn() {
     return [this] -> _EntryIters { return entry_iters(); };
   }
   _LIBCPP_HIDE_FROM_ABI auto entry_append_fn() {
-    return [this] -> __stacktrace::entry_base& { return entry_append(); };
+    return [this] -> __stacktrace::_Entry& { return entry_append(); };
   }
 
 public:
@@ -202,16 +202,16 @@ class basic_stacktrace : private __stacktrace::base {
 
   _LIBCPP_HIDE_FROM_ABI constexpr ~basic_stacktrace() = default;
 
-  static_assert(sizeof(__stacktrace::entry_base) == sizeof(stacktrace_entry));
+  static_assert(sizeof(__stacktrace::_Entry) == sizeof(stacktrace_entry));
 
   _LIBCPP_HIDE_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc)
-      : base(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__alloc_) {}
+      : _Trace(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__alloc_) {}
 
   _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
-      : base(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__other.__entries_) {}
+      : _Trace(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__other.__entries_) {}
 
   _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
-      : base(__alloc, entry_iters_fn(), entry_append_fn()),
+      : _Trace(__alloc, entry_iters_fn(), entry_append_fn()),
         __alloc_(__alloc),
         __entries_(std::move(__other.__entries_)) {}
 
@@ -337,11 +337,11 @@ swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b) noexc
 #  if _LIBCPP_HAS_LOCALIZATION
 template <class _Allocator>
 _LIBCPP_HIDE_FROM_ABI inline ostream& operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __stacktrace) {
-  return ((__stacktrace::base const&)__stacktrace).write_to(__os);
+  return ((__stacktrace::_Trace const&)__stacktrace).write_to(__os);
 }
 template <class _Allocator>
 _LIBCPP_HIDE_FROM_ABI inline string to_string(const basic_stacktrace<_Allocator>& __stacktrace) {
-  return ((__stacktrace::base const&)__stacktrace).to_string();
+  return ((__stacktrace::_Trace const&)__stacktrace).to_string();
 }
 #  endif // _LIBCPP_HAS_LOCALIZATION
 
@@ -363,12 +363,12 @@ namespace __stacktrace {
 
 #  if defined(_WIN32)
 
-_LIBCPP_EXPORTED_FROM_ABI void base::windows_impl(size_t skip, size_t max_depth)
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::windows_impl(size_t skip, size_t max_depth)
 
 #  else
 
-struct unwind_backtrace {
-  base& base_;
+struct _Unwind_Wrapper {
+  _Trace& base_;
   size_t skip_;
   size_t maxDepth_;
 
@@ -387,22 +387,22 @@ struct unwind_backtrace {
       return _Unwind_Reason_Code::_URC_NORMAL_STOP;
     }
     auto& __entry = base_.__entry_append_();
-    auto& __eb    = (entry_base&)__entry;
+    auto& __eb    = (_Entry&)__entry;
     __eb.__addr_  = (__ip_before ? __ip : __ip - 1);
     return _Unwind_Reason_Code::_URC_NO_REASON;
   }
 
   _LIBCPP_HIDE_FROM_ABI static _Unwind_Reason_Code callback(_Unwind_Context* __cx, void* __self) {
-    return ((unwind_backtrace*)__self)->callback(__cx);
+    return ((_Unwind_Wrapper*)__self)->callback(__cx);
   }
 };
 
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void base::unwind_addrs(size_t __skip, size_t __depth) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void _Trace::unwind_addrs(size_t __skip, size_t __depth) {
   if (!__depth) {
     return;
   }
-  unwind_backtrace __bt{*this, __skip, __depth};
-  _Unwind_Backtrace(unwind_backtrace::callback, &__bt);
+  _Unwind_Wrapper __bt{*this, __skip, __depth};
+  _Unwind_Backtrace(_Unwind_Wrapper::callback, &__bt);
 }
 
 #  endif // _WIN32
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index 1168195a56de0..6dc88bcf40a1e 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -40,10 +40,10 @@ class stacktrace_entry;
 
 namespace __stacktrace {
 
-struct str;
+struct _Str;
 
 template <class _Tp>
-struct str_alloc {
+struct _Str_Alloc {
   using result_t _LIBCPP_NODEBUG = allocation_result<_Tp*, size_t>;
 
   // Lambdas wrap the caller's allocator, re-bound so we can deal with `chars`.
@@ -58,22 +58,22 @@ struct str_alloc {
 
   template <class _Tp2>
   struct rebind {
-    using other = str_alloc<_Tp2>;
+    using other = _Str_Alloc<_Tp2>;
   };
 
-  str_alloc(const str_alloc&)            = default;
-  str_alloc(str_alloc&&)                 = default;
-  str_alloc& operator=(const str_alloc&) = default;
-  str_alloc& operator=(str_alloc&&)      = default;
+  _Str_Alloc(const _Str_Alloc&)            = default;
+  _Str_Alloc(_Str_Alloc&&)                 = default;
+  _Str_Alloc& operator=(const _Str_Alloc&) = default;
+  _Str_Alloc& operator=(_Str_Alloc&&)      = default;
 
-  str_alloc(function<char*(size_t)> __alloc, function<void(char*, size_t)> __dealloc)
+  _Str_Alloc(function<char*(size_t)> __alloc, function<void(char*, size_t)> __dealloc)
       : __alloc_(std::move(__alloc)), __dealloc_(std::move(__dealloc)) {}
 
   template <class _A0, // some allocator; can be of any type
             class _AT = allocator_traits<_A0>,
             class _CA = typename _AT::template rebind_alloc<char>>
     requires __is_allocator_v<_A0>
-  static str_alloc make(_A0 __a) {
+  static _Str_Alloc make(_A0 __a) {
     auto __ca = _CA(__a);
     return {[__ca](size_t __n) mutable -> char* { return __ca.allocate(__n); },
             [__ca](char* __p, size_t __n) mutable { __ca.deallocate(__p, __n); }};
@@ -81,18 +81,18 @@ struct str_alloc {
 
   _Tp* allocate(size_t __n) { return __alloc_(__n); }
   void deallocate(_Tp* __p, size_t __n) { __dealloc_(__p, __n); }
-  bool operator==(str_alloc<_Tp> const& __rhs) const { return std::addressof(__rhs) == this; }
+  bool operator==(_Str_Alloc<_Tp> const& __rhs) const { return std::addressof(__rhs) == this; }
 };
 
-struct str : basic_string<char, char_traits<char>, str_alloc<char>> {
-  using base = basic_string<char, char_traits<char>, str_alloc<char>>;
-  _LIBCPP_HIDE_FROM_ABI str(str_alloc<char> const& __alloc) : base(__alloc) {}
+struct _Str : basic_string<char, char_traits<char>, _Str_Alloc<char>> {
+  using base = basic_string<char, char_traits<char>, _Str_Alloc<char>>;
+  _LIBCPP_HIDE_FROM_ABI _Str(_Str_Alloc<char> const& __alloc) : base(__alloc) {}
   _LIBCPP_HIDE_FROM_ABI string_view view() const { return {this->data(), this->size()}; }
 };
 
-struct image;
+struct _Image;
 
-struct entry_base {
+struct _Entry {
   constexpr static size_t __max_sym_len = 512;
 #  if defined(PATH_MAX)
   constexpr static size_t __max_file_len = PATH_MAX;
@@ -103,13 +103,13 @@ struct entry_base {
 #  endif
 
   uintptr_t __addr_{};
-  optional<str> __desc_{};
-  optional<str> __file_{};
+  optional<_Str> __desc_{};
+  optional<_Str> __file_{};
   uint_least32_t __line_{};
-  image const* __image_{};
+  _Image const* __image_{};
 
-  _LIBCPP_HIDE_FROM_ABI str& assign_desc(str&& __s) { return *(__desc_ = std::move(__s)); }
-  _LIBCPP_HIDE_FROM_ABI str& assign_file(str&& __s) { return *(__file_ = std::move(__s)); }
+  _LIBCPP_HIDE_FROM_ABI _Str& assign_desc(_Str&& __s) { return *(__desc_ = std::move(__s)); }
+  _LIBCPP_HIDE_FROM_ABI _Str& assign_file(_Str&& __s) { return *(__file_ = std::move(__s)); }
 
 #  if _LIBCPP_HAS_LOCALIZATION
   _LIBCPP_EXPORTED_FROM_ABI std::ostream& write_to(std::ostream& __os) const;
@@ -118,14 +118,14 @@ struct entry_base {
 
   _LIBCPP_HIDE_FROM_ABI uintptr_t adjusted_addr() const;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr static entry_base* of(auto& __s) { return static_cast<entry_base*>(__s); }
+  _LIBCPP_HIDE_FROM_ABI constexpr static _Entry* of(auto& __s) { return static_cast<_Entry*>(__s); }
 
-  _LIBCPP_HIDE_FROM_ABI ~entry_base()                                      = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr entry_base()                             = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr entry_base(const entry_base&)            = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr entry_base& operator=(const entry_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr entry_base(entry_base&&)                 = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr entry_base& operator=(entry_base&&)      = default;
+  _LIBCPP_HIDE_FROM_ABI ~_Entry()                                  = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr _Entry()                         = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr _Entry(const _Entry&)            = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr _Entry& operator=(const _Entry&) = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr _Entry(_Entry&&)                 = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr _Entry& operator=(_Entry&&)      = default;
 };
 
 } // namespace __stacktrace
@@ -134,7 +134,7 @@ class stacktrace_entry {
   friend _LIBCPP_HIDE_FROM_ABI inline ostream& operator<<(ostream& __os, std::stacktrace_entry const& __entry);
   friend _LIBCPP_HIDE_FROM_ABI inline string to_string(std::stacktrace_entry const& __entry);
 
-  __stacktrace::entry_base __base_{};
+  __stacktrace::_Entry __base_{};
 
 public:
   // (19.6.3.1) Overview [stacktrace.entry.overview]
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 ba952095d16a1..b11c50254906c 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
@@ -413,10 +413,10 @@
 {'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace10entry_base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace10entry_base9to_stringEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace4base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace4base9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Entry8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Entry9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Trace8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Trace9to_stringEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__112bad_weak_ptr4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm', 'type': 'FUNC'}
@@ -979,11 +979,11 @@
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base11find_imagesEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base12find_symbolsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base16find_source_locsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace11find_imagesEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace12find_symbolsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace16find_source_locsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace7_ImagesC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace7_ImagesC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD2Ev', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 04c2115f96d7d..8b8fde6b28d92 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -104,10 +104,10 @@
 {'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base9to_stringEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace6_Entry8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace6_Entry9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace6_Trace8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace6_Trace9to_stringEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__112bad_weak_ptr4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm', 'type': 'FUNC'}
@@ -627,11 +627,11 @@
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base11find_imagesEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base12find_symbolsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base16find_source_locsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace11find_imagesEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace12find_symbolsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace16find_source_locsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace7_ImagesC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace7_ImagesC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD2Ev', 'type': 'FUNC'}
diff --git a/libcxx/src/stacktrace/images.cpp b/libcxx/src/stacktrace/images.cpp
index 4120ff409bb3c..391d3304523a3 100644
--- a/libcxx/src/stacktrace/images.cpp
+++ b/libcxx/src/stacktrace/images.cpp
@@ -24,7 +24,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-images::images() {
+_Images::_Images() {
   images_[count_++] = {0uz, 0};  // sentinel at low end
   images_[count_++] = {~0uz, 0}; // sentinel at high end
   auto dyld_count   = _dyld_image_count();
@@ -55,8 +55,8 @@ namespace __stacktrace {
 
 namespace {
 int add_image(dl_phdr_info* info, size_t, void* images_v) {
-  auto& imgs = *(images*)images_v;
-  if (imgs.count_ == images::k_max_images) {
+  auto& imgs = *(_Images*)images_v;
+  if (imgs.count_ == _Images::k_max_images) {
     return 0;
   }
   auto is_first = (imgs.count_ == 0);
@@ -69,17 +69,17 @@ int add_image(dl_phdr_info* info, size_t, void* images_v) {
   // `dl_iterate_phdr` gives us the main program image first
   image.is_main_prog_ = is_first;
   if (!image.name_[0] && is_first) {
-    char buf[entry_base::__max_file_len]{0};
+    char buf[_Entry::__max_file_len]{0};
     if (readlink("/proc/self/exe", buf, sizeof(buf)) != -1) { // Ignores errno if error
       strncpy(image.name_, buf, sizeof(image.name_));
     }
   }
   // If we're at the limit, return nonzero to stop iterating
-  return imgs.count_ == images::k_max_images;
+  return imgs.count_ == _Images::k_max_images;
 }
 } // namespace
 
-images::images() {
+_Images::_Images() {
   dl_iterate_phdr(add_image, this);
   images_[count_++] = {0uz, 0};  // sentinel at low end
   images_[count_++] = {~0uz, 0}; // sentinel at high end
diff --git a/libcxx/src/stacktrace/images.h b/libcxx/src/stacktrace/images.h
index 476d684734878..1af64fae6f9c8 100644
--- a/libcxx/src/stacktrace/images.h
+++ b/libcxx/src/stacktrace/images.h
@@ -26,16 +26,16 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct image;
-struct images;
+struct _Image;
+struct _Images;
 
-struct image {
+struct _Image {
   uintptr_t loaded_at_{};
   uintptr_t slide_{};
-  char name_[__stacktrace::entry_base::__max_file_len]{0};
+  char name_[__stacktrace::_Entry::__max_file_len]{0};
   bool is_main_prog_{};
 
-  _LIBCPP_HIDE_FROM_ABI bool operator<(image const& __rhs) const {
+  _LIBCPP_HIDE_FROM_ABI bool operator<(_Image const& __rhs) const {
     if (loaded_at_ < __rhs.loaded_at_) {
       return true;
     }
@@ -58,22 +58,22 @@ struct image {
  *  (sentinel)      foo.exe         libc++so.1      libc.so.6         (sentinel)
  *  0x000000000000  0x000100000000  0x633b00000000  0x7c5500000000    0xffffffffffff
  */
-struct images {
+struct _Images {
   constexpr static size_t k_max_images = 256;
-  std::array<image, k_max_images + 2> images_{}; // space for the L/R sentinels
-  unsigned count_{0};                            // image count, including sentinels
+  std::array<_Image, k_max_images + 2> images_{}; // space for the L/R sentinels
+  unsigned count_{0};                             // image count, including sentinels
 
   /** An OS-specific constructor is defined. */
-  _LIBCPP_EXPORTED_FROM_ABI images();
+  _LIBCPP_EXPORTED_FROM_ABI _Images();
 
   /** Get prog_image by index (0 <= index < count_) */
-  _LIBCPP_HIDE_FROM_ABI image& operator[](size_t __index) {
+  _LIBCPP_HIDE_FROM_ABI _Image& operator[](size_t __index) {
     _LIBCPP_ASSERT(__index < count_, "index out of range");
     return images_.at(__index);
   }
 
   /** Image representing the main program, or nullptr if we couldn't find it */
-  _LIBCPP_HIDE_FROM_ABI image* main_prog_image() {
+  _LIBCPP_HIDE_FROM_ABI _Image* main_prog_image() {
     for (size_t __i = 1; __i < count_ - 1; __i++) {
       auto& __image = images_[__i];
       if (__image.is_main_prog_) {
diff --git a/libcxx/src/stacktrace/impl.cpp b/libcxx/src/stacktrace/impl.cpp
index c3b9f906a4cf3..15f3d3bb86a4c 100644
--- a/libcxx/src/stacktrace/impl.cpp
+++ b/libcxx/src/stacktrace/impl.cpp
@@ -25,7 +25,7 @@ namespace __stacktrace {
 
 #if _LIBCPP_HAS_LOCALIZATION
 
-_LIBCPP_EXPORTED_FROM_ABI ostream& entry_base::write_to(ostream& __os) const {
+_LIBCPP_EXPORTED_FROM_ABI ostream& _Entry::write_to(ostream& __os) const {
   // Although 64-bit addresses are 16 nibbles long, they're often <= 0x7fff_ffff_ffff
   constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
 
@@ -42,7 +42,7 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& entry_base::write_to(ostream& __os) const {
   return __os;
 }
 
-_LIBCPP_EXPORTED_FROM_ABI ostream& base::write_to(std::ostream& __os) const {
+_LIBCPP_EXPORTED_FROM_ABI ostream& _Trace::write_to(std::ostream& __os) const {
   auto iters = __entry_iters_();
   auto count = iters.size();
   if (!count) {
@@ -60,13 +60,13 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& base::write_to(std::ostream& __os) const {
   return __os;
 }
 
-_LIBCPP_EXPORTED_FROM_ABI string entry_base::to_string() const {
+_LIBCPP_EXPORTED_FROM_ABI string _Entry::to_string() const {
   stringstream __ss;
   write_to(__ss);
   return __ss.str();
 }
 
-_LIBCPP_EXPORTED_FROM_ABI string base::to_string() const {
+_LIBCPP_EXPORTED_FROM_ABI string _Trace::to_string() const {
   stringstream __ss;
   write_to(__ss);
   return __ss.str();
@@ -74,7 +74,7 @@ _LIBCPP_EXPORTED_FROM_ABI string base::to_string() const {
 
 #endif // _LIBCPP_HAS_LOCALIZATION
 
-_LIBCPP_HIDE_FROM_ABI uintptr_t entry_base::adjusted_addr() const {
+_LIBCPP_HIDE_FROM_ABI uintptr_t _Entry::adjusted_addr() const {
   auto sub = __image_ ? __image_->slide_ : 0;
   return __addr_ - sub;
 }
diff --git a/libcxx/src/stacktrace/impl_generic.cpp b/libcxx/src/stacktrace/impl_generic.cpp
index ae3624def0816..8b8fc66c20e90 100644
--- a/libcxx/src/stacktrace/impl_generic.cpp
+++ b/libcxx/src/stacktrace/impl_generic.cpp
@@ -20,8 +20,8 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-_LIBCPP_EXPORTED_FROM_ABI void base::find_images() {
-  images images;
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_images() {
+  _Images images;
   size_t i = 0;
   for (auto& entry : __entry_iters_()) {
     images.find(&i, entry.__addr_);
@@ -33,11 +33,11 @@ _LIBCPP_EXPORTED_FROM_ABI void base::find_images() {
   }
 }
 
-_LIBCPP_EXPORTED_FROM_ABI void base::find_symbols() {
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_symbols() {
   // TODO
 }
 
-_LIBCPP_EXPORTED_FROM_ABI void base::find_source_locs() {
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_source_locs() {
   // TODO
 }
 
diff --git a/libcxx/src/stacktrace/impl_windows.cpp b/libcxx/src/stacktrace/impl_windows.cpp
index b31ecee8f1ee6..10258c664d0a1 100644
--- a/libcxx/src/stacktrace/impl_windows.cpp
+++ b/libcxx/src/stacktrace/impl_windows.cpp
@@ -24,13 +24,13 @@ namespace __stacktrace {
 
 namespace {
 
-struct dll {
+struct _DLL {
   HMODULE module_{};
   bool loaded_{};
 
-  explicit dll(char const* name) : module_(LoadLibraryA(name)) {}
+  explicit _DLL(char const* name) : module_(LoadLibraryA(name)) {}
 
-  ~dll() {
+  ~_DLL() {
     if (module_) {
       FreeLibrary(module_);
     }
@@ -44,7 +44,7 @@ struct dll {
 
 // clang-format off
 
-struct dbghelp_dll final : dll {
+struct _Dbghelp_DLL final : _DLL {
   IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
   bool    (WINAPI *SymCleanup)         (HANDLE);
   DWORD   (WINAPI *SymGetOptions)      ();
@@ -60,7 +60,7 @@ struct dbghelp_dll final : dll {
   bool    (WINAPI *SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
   DWORD64 (WINAPI *SymLoadModule64)    (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
 
-  dbghelp_dll() : dll("dbghelp.dll") {
+  _Dbghelp_DLL() : _DLL("dbghelp.dll") {
     loaded_ = true
       && get_func(&ImageNtHeader, "ImageNtHeader")
       && get_func(&SymCleanup, "SymCleanup")
@@ -80,12 +80,12 @@ struct dbghelp_dll final : dll {
   }
 };
 
-struct psapi_dll final : dll {
+struct _Psapi_DLL final : _DLL {
   bool  (WINAPI *EnumProcessModules)   (HANDLE, HMODULE*, DWORD, DWORD*);
   bool  (WINAPI *GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
   DWORD (WINAPI *GetModuleBaseName)    (HANDLE, HMODULE, char**, DWORD);
 
-  psapi_dll() : dll("psapi.dll") {
+  _Psapi_DLL() : _DLL("psapi.dll") {
     loaded_ = true
       && get_func(&EnumProcessModules, "EnumProcessModules")
       && get_func(&GetModuleInformation, "GetModuleInformation")
@@ -94,15 +94,15 @@ struct psapi_dll final : dll {
   }
 };
 
-struct sym_init_scope {
-  dbghelp_dll& dbghelp_;
+struct _Sym_Init_Scope {
+  _Dbghelp_DLL& dbghelp_;
   HANDLE proc_;
 
-  sym_init_scope(dbghelp_dll& dbghelp, HANDLE proc)
+  _Sym_Init_Scope(_Dbghelp_DLL& dbghelp, HANDLE proc)
     : dbghelp_(dbghelp), proc_(proc) {
     (*dbghelp_.SymInitialize)(proc_, nullptr, true);
   }
-  ~sym_init_scope() {
+  ~_Sym_Init_Scope() {
     (*dbghelp_.SymCleanup)(proc_);
   }
 };
@@ -110,13 +110,13 @@ struct sym_init_scope {
 }  // namespace
 
 _LIBCPP_EXPORTED_FROM_ABI void
-base::windows_impl(size_t skip, size_t max_depth) {
+_Trace::windows_impl(size_t skip, size_t max_depth) {
   if (!max_depth) [[unlikely]] {
     return;
   }
 
-  static psapi_dll psapi;
-  static dbghelp_dll dbghelp;
+  static _Psapi_DLL psapi;
+  static _Dbghelp_DLL dbghelp;
   if (!psapi.loaded_ || !dbghelp.loaded_) { return; }
 
   // Not thread-safe according to docs
@@ -127,7 +127,7 @@ base::windows_impl(size_t skip, size_t max_depth) {
   HMODULE exe = GetModuleHandle(nullptr);
   if (!exe) { return; }
 
-  sym_init_scope symscope(dbghelp, proc);
+  _Sym_Init_Scope symscope(dbghelp, proc);
 
   char sym_path[MAX_PATH * 4]; // arbitrary
   if (!(*dbghelp.SymGetSearchPath)(proc, sym_path, sizeof(sym_path))) { return; }

>From 4f800883bcd78598513513a4ff339ff06ebefdbd Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 27 Oct 2025 10:05:18 -0400
Subject: [PATCH 8/9] Split up impl's to make more readable.  Simplify things
 considerably

---
 .../include/__stacktrace/basic_stacktrace.h   | 167 +++++-------------
 .../include/__stacktrace/stacktrace_entry.h   |  89 +++-------
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   6 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   4 +-
 libcxx/src/CMakeLists.txt                     |  10 +-
 .../{images.cpp => default_impl.cpp}          |  57 ++----
 libcxx/src/stacktrace/{impl.cpp => entry.cpp} |  32 +---
 .../{impl_generic.cpp => macos_impl.cpp}      |  36 ++--
 libcxx/src/stacktrace/trace.cpp               |  54 ++++++
 .../{impl_windows.cpp => windows_impl.cpp}    |   6 +-
 .../test/libcxx/transitive_includes/cxx23.csv |   2 -
 .../test/libcxx/transitive_includes/cxx26.csv |   2 -
 12 files changed, 184 insertions(+), 281 deletions(-)
 rename libcxx/src/stacktrace/{images.cpp => default_impl.cpp} (61%)
 rename libcxx/src/stacktrace/{impl.cpp => entry.cpp} (65%)
 rename libcxx/src/stacktrace/{impl_generic.cpp => macos_impl.cpp} (52%)
 create mode 100644 libcxx/src/stacktrace/trace.cpp
 rename libcxx/src/stacktrace/{impl_windows.cpp => windows_impl.cpp} (97%)

diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index 1d343f95a6938..dbc6f7f4b0708 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -67,48 +67,24 @@ struct _Trace {
   constexpr static size_t __default_max_depth  = 64;
   constexpr static size_t __absolute_max_depth = 256;
 
-  _Str_Alloc<char> __string_alloc_;
-
   using _EntryIters _LIBCPP_NODEBUG = _Iters<stacktrace_entry, _Entry>;
   function<_EntryIters()> __entry_iters_;
   function<_Entry&()> __entry_append_;
 
-  template <class _Allocator>
-  _LIBCPP_HIDE_FROM_ABI
-  _Trace(_Allocator const& __alloc, function<_EntryIters()> __entry_iters, function<_Entry&()> __entry_append)
-      : __string_alloc_(std::move(_Str_Alloc<char>::make(__alloc))),
-        __entry_iters_(__entry_iters),
-        __entry_append_(__entry_append) {}
-
-#  ifndef _WIN32
-  // Use <unwind.h> or <libunwind.h> to collect addresses in the stack.
-  // Defined in this header so it can be reliably force-inlined.
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE void unwind_addrs(size_t __skip, size_t __depth);
-#  endif
-
-  // On Windows, there are DLLs which take care of all this (dbghelp, psapi), with
-  // zero overlap with any other OS, so it's in its own file, impl_windows.cpp.
-  // For all other platforms we implement these "find" methods.
-
-  // Resolve instruction addresses to their respective images, dealing with possible ASLR.
-  _LIBCPP_EXPORTED_FROM_ABI void find_images();
-
-  // Resolve addresses to symbols if possible.
-  _LIBCPP_EXPORTED_FROM_ABI void find_symbols();
-
-  // Resolve addresses to source locations if possible.
-  _LIBCPP_EXPORTED_FROM_ABI void find_source_locs();
-
-  _LIBCPP_HIDE_FROM_ABI void finish_stacktrace() {
-    find_images();
-    find_symbols();
-    find_source_locs();
-  }
+  _LIBCPP_HIDE_FROM_ABI _Trace(function<_EntryIters()> __entry_iters, function<_Entry&()> __entry_append)
+      : __entry_iters_(__entry_iters), __entry_append_(__entry_append) {}
 
   _LIBCPP_EXPORTED_FROM_ABI ostream& write_to(ostream& __os) const;
   _LIBCPP_EXPORTED_FROM_ABI string to_string() const;
 
-  _LIBCPP_HIDE_FROM_ABI _Str __create_str() { return _Str(__string_alloc_); }
+#  ifdef _WIN32
+  // Windows impl uses dbghelp and psapi DLLs to do the full stacktrace operation.
+  _LIBCPP_EXPORTED_FROM_ABI void windows_impl(size_t skip, size_t max_depth);
+#  else
+  // Non-windows: impl separated out into several smaller platform-dependent parts.
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE void populate_addrs(size_t __skip, size_t __depth);
+  _LIBCPP_EXPORTED_FROM_ABI void populate_images();
+#  endif
 };
 
 } // namespace __stacktrace
@@ -123,15 +99,6 @@ class basic_stacktrace : private __stacktrace::_Trace {
   friend struct hash<basic_stacktrace<_Allocator>>;
   friend struct __stacktrace::_Trace;
 
-  using _ATraits _LIBCPP_NODEBUG            = allocator_traits<_Allocator>;
-  constexpr static bool __kPropOnCopyAssign = _ATraits::propagate_on_container_copy_assignment::value;
-  constexpr static bool __kPropOnMoveAssign = _ATraits::propagate_on_container_move_assignment::value;
-  constexpr static bool __kPropOnSwap       = _ATraits::propagate_on_container_swap::value;
-  constexpr static bool __kAlwaysEqual      = _ATraits::is_always_equal::value;
-  constexpr static bool __kNoThrowAlloc     = noexcept(noexcept(_Allocator().allocate(1)));
-
-  _LIBCPP_NO_UNIQUE_ADDRESS _Allocator __alloc_;
-
   vector<stacktrace_entry, _Allocator> __entries_;
   _LIBCPP_HIDE_FROM_ABI _EntryIters entry_iters() { return {__entries_.data(), __entries_.size()}; }
   _LIBCPP_HIDE_FROM_ABI __stacktrace::_Entry& entry_append() {
@@ -152,106 +119,71 @@ class basic_stacktrace : private __stacktrace::_Trace {
   using value_type             = stacktrace_entry;
   using const_reference        = value_type const&;
   using reference              = value_type&;
+  using const_iterator         = decltype(__entries_.cbegin());
+  using iterator               = const_iterator;
+  using reverse_iterator       = std::reverse_iterator<iterator>;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
   using difference_type        = ptrdiff_t;
   using size_type              = size_t;
   using allocator_type         = _Allocator;
-  using iterator               = decltype(__entries_.begin());
-  using const_iterator         = decltype(__entries_.cbegin());
-  using reverse_iterator       = decltype(__entries_.rbegin());
-  using const_reverse_iterator = decltype(__entries_.crbegin());
 
   // (19.6.4.2)
   // Creation and assignment [stacktrace.basic.cons]
 
-  _LIBCPP_ALWAYS_INLINE static basic_stacktrace
-  current(const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
-    size_type __skip      = 0;
-    size_type __max_depth = __default_max_depth;
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
-        __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
-    basic_stacktrace __ret{__caller_alloc};
-    __ret.unwind_addrs(__skip, __max_depth);
-    __ret.finish_stacktrace();
-    return __ret;
+  _LIBCPP_ALWAYS_INLINE // Omit this function from the trace
+  static basic_stacktrace current(const allocator_type& __alloc = allocator_type()) noexcept {
+    return current(0, __default_max_depth, __alloc);
   }
 
-  _LIBCPP_ALWAYS_INLINE static basic_stacktrace
-  current(size_type __skip, const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
-    size_type __max_depth = __default_max_depth;
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
-        __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
-    basic_stacktrace __ret{__caller_alloc};
-    __ret.unwind_addrs(__skip, __max_depth);
-    __ret.finish_stacktrace();
-    return __ret;
+  _LIBCPP_ALWAYS_INLINE // Omit this function from the trace
+  static basic_stacktrace current(size_type __skip, const allocator_type& __alloc = allocator_type()) noexcept {
+    return current(__skip, __default_max_depth, __alloc);
   }
 
-  _LIBCPP_ALWAYS_INLINE static basic_stacktrace
-  current(size_type __skip,
-          size_type __max_depth,
-          const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+  _LIBCPP_ALWAYS_INLINE // Omit this function from the trace
+  static basic_stacktrace
+  current(size_type __skip, size_type __max_depth, const allocator_type& __alloc = allocator_type()) noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
         __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
-    basic_stacktrace __ret{__caller_alloc};
-    if (__max_depth) [[likely]] {
-      __ret.unwind_addrs(__skip, __max_depth);
-      __ret.finish_stacktrace();
+    basic_stacktrace __ret{__alloc};
+
+    if (__max_depth) {
+#  if defined(WIN32)
+      __ret.windows_impl(__skip, __max_depth);
+#  else
+      __ret.populate_addrs(__skip, __max_depth);
+      __ret.populate_images();
+#  endif
     }
+
     return __ret;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr ~basic_stacktrace() = default;
-
-  static_assert(sizeof(__stacktrace::_Entry) == sizeof(stacktrace_entry));
-
   _LIBCPP_HIDE_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc)
-      : _Trace(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__alloc_) {}
+      : _Trace(entry_iters_fn(), entry_append_fn()), __entries_(__alloc) {}
 
   _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
-      : _Trace(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__other.__entries_) {}
+      : _Trace(entry_iters_fn(), entry_append_fn()), __entries_(__other.__entries_, __alloc) {}
 
   _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
-      : _Trace(__alloc, entry_iters_fn(), entry_append_fn()),
-        __alloc_(__alloc),
-        __entries_(std::move(__other.__entries_)) {}
+      : _Trace(entry_iters_fn(), entry_append_fn()), __entries_(std::move(__other.__entries_), __alloc) {}
 
   _LIBCPP_HIDE_FROM_ABI basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>)
       : basic_stacktrace(allocator_type()) {}
 
-  _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other) noexcept
-      : basic_stacktrace(__other, _ATraits::select_on_container_copy_construction(__other.__alloc_)) {}
-
-  _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept
-      : basic_stacktrace(std::move(__other), __other.__alloc_) {}
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other) noexcept       = default;
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept            = default;
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace& operator=(const basic_stacktrace& __other)     = default;
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace& operator=(basic_stacktrace&& __other) noexcept = default;
 
-  _LIBCPP_HIDE_FROM_ABI basic_stacktrace& operator=(const basic_stacktrace& __other) {
-    if (std::addressof(__other) != this) {
-      if (__kPropOnCopyAssign) {
-        new (this) basic_stacktrace(__other, __other.__alloc_);
-      } else {
-        new (this) basic_stacktrace(__other);
-      }
-    }
-    return *this;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI basic_stacktrace&
-  operator=(basic_stacktrace&& __other) noexcept(__kPropOnMoveAssign || __kAlwaysEqual) {
-    if (std::addressof(__other) != this) {
-      if (__kPropOnMoveAssign) {
-        auto __alloc = __other.__alloc_;
-        new (this) basic_stacktrace(std::move(__other), __alloc);
-      } else {
-        new (this) basic_stacktrace(std::move(__other));
-      }
-    }
-    return *this;
-  }
+  _LIBCPP_HIDE_FROM_ABI ~basic_stacktrace() = default;
 
   // (19.6.4.3)
   // [stacktrace.basic.obs], observers
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __alloc_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept {
+    return __entries_.get_allocator();
+  }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const noexcept { return __entries_.begin(); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const noexcept { return __entries_.end(); }
@@ -313,9 +245,6 @@ class basic_stacktrace : private __stacktrace::_Trace {
       allocator_traits<_Allocator>::propagate_on_container_swap::value ||
       allocator_traits<_Allocator>::is_always_equal::value) {
     std::swap(__entries_, __other.__entries_);
-    if (__kPropOnSwap) {
-      std::swap(__alloc_, __other.__alloc_);
-    }
   }
 };
 
@@ -361,11 +290,7 @@ struct hash<basic_stacktrace<_Allocator>> {
 
 namespace __stacktrace {
 
-#  if defined(_WIN32)
-
-_LIBCPP_EXPORTED_FROM_ABI void _Trace::windows_impl(size_t skip, size_t max_depth)
-
-#  else
+#  if !defined(_WIN32)
 
 struct _Unwind_Wrapper {
   _Trace& base_;
@@ -397,7 +322,7 @@ struct _Unwind_Wrapper {
   }
 };
 
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void _Trace::unwind_addrs(size_t __skip, size_t __depth) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void _Trace::populate_addrs(size_t __skip, size_t __depth) {
   if (!__depth) {
     return;
   }
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index 6dc88bcf40a1e..b3f34a245b512 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -11,6 +11,7 @@
 #define _LIBCPP_STACKTRACE_ENTRY_H
 
 #include <__config>
+#include <cstring>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -23,9 +24,8 @@ _LIBCPP_PUSH_MACROS
 #include <__functional/function.h>
 #include <cstddef>
 #include <cstdint>
-#include <memory>
-#include <optional>
 #include <string>
+#include <string_view>
 
 #if _LIBCPP_HAS_LOCALIZATION
 #  include <__fwd/format.h>
@@ -40,58 +40,30 @@ class stacktrace_entry;
 
 namespace __stacktrace {
 
-struct _Str;
-
-template <class _Tp>
-struct _Str_Alloc {
-  using result_t _LIBCPP_NODEBUG = allocation_result<_Tp*, size_t>;
-
-  // Lambdas wrap the caller's allocator, re-bound so we can deal with `chars`.
-  function<char*(size_t)> __alloc_;
-  function<void(char*, size_t)> __dealloc_;
-
-  // This only works with chars or other 1-byte things.
-  static_assert(sizeof(_Tp) == 1);
-
-  using value_type = _Tp;
-  using pointer    = _Tp*;
-
-  template <class _Tp2>
-  struct rebind {
-    using other = _Str_Alloc<_Tp2>;
-  };
-
-  _Str_Alloc(const _Str_Alloc&)            = default;
-  _Str_Alloc(_Str_Alloc&&)                 = default;
-  _Str_Alloc& operator=(const _Str_Alloc&) = default;
-  _Str_Alloc& operator=(_Str_Alloc&&)      = default;
-
-  _Str_Alloc(function<char*(size_t)> __alloc, function<void(char*, size_t)> __dealloc)
-      : __alloc_(std::move(__alloc)), __dealloc_(std::move(__dealloc)) {}
+struct _Image;
 
-  template <class _A0, // some allocator; can be of any type
-            class _AT = allocator_traits<_A0>,
-            class _CA = typename _AT::template rebind_alloc<char>>
-    requires __is_allocator_v<_A0>
-  static _Str_Alloc make(_A0 __a) {
-    auto __ca = _CA(__a);
-    return {[__ca](size_t __n) mutable -> char* { return __ca.allocate(__n); },
-            [__ca](char* __p, size_t __n) mutable { __ca.deallocate(__p, __n); }};
+struct _StringWrapper {
+  // XXX FIXME TODO:
+  // Figure out a solution for creating strings while respecting
+  // the caller's allocator they provided:
+  //   1. properly type-erase basic_strings' allocator types
+  //   2. move all code into headers (seems like a bad idea)
+  //   3. leave these as oversized char arrays, seems suboptimal
+  //   4. just use std::string, which is just plain wrong
+  //   5. ...?
+
+  char __chars_[1024];
+
+  _LIBCPP_EXPORTED_FROM_ABI std::string_view view() const { return __chars_; }
+
+  _LIBCPP_EXPORTED_FROM_ABI _StringWrapper& assign(std::string_view __view) {
+    size_t __size = std::min(__view.size(), sizeof(__chars_) - 1);
+    memcpy(__chars_, __view.data(), __size);
+    __chars_[__size] = 0;
+    return *this;
   }
-
-  _Tp* allocate(size_t __n) { return __alloc_(__n); }
-  void deallocate(_Tp* __p, size_t __n) { __dealloc_(__p, __n); }
-  bool operator==(_Str_Alloc<_Tp> const& __rhs) const { return std::addressof(__rhs) == this; }
 };
 
-struct _Str : basic_string<char, char_traits<char>, _Str_Alloc<char>> {
-  using base = basic_string<char, char_traits<char>, _Str_Alloc<char>>;
-  _LIBCPP_HIDE_FROM_ABI _Str(_Str_Alloc<char> const& __alloc) : base(__alloc) {}
-  _LIBCPP_HIDE_FROM_ABI string_view view() const { return {this->data(), this->size()}; }
-};
-
-struct _Image;
-
 struct _Entry {
   constexpr static size_t __max_sym_len = 512;
 #  if defined(PATH_MAX)
@@ -103,14 +75,11 @@ struct _Entry {
 #  endif
 
   uintptr_t __addr_{};
-  optional<_Str> __desc_{};
-  optional<_Str> __file_{};
+  _StringWrapper __desc_{};
+  _StringWrapper __file_{};
   uint_least32_t __line_{};
   _Image const* __image_{};
 
-  _LIBCPP_HIDE_FROM_ABI _Str& assign_desc(_Str&& __s) { return *(__desc_ = std::move(__s)); }
-  _LIBCPP_HIDE_FROM_ABI _Str& assign_file(_Str&& __s) { return *(__file_ = std::move(__s)); }
-
 #  if _LIBCPP_HAS_LOCALIZATION
   _LIBCPP_EXPORTED_FROM_ABI std::ostream& write_to(std::ostream& __os) const;
   _LIBCPP_EXPORTED_FROM_ABI string to_string() const;
@@ -118,8 +87,6 @@ struct _Entry {
 
   _LIBCPP_HIDE_FROM_ABI uintptr_t adjusted_addr() const;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr static _Entry* of(auto& __s) { return static_cast<_Entry*>(__s); }
-
   _LIBCPP_HIDE_FROM_ABI ~_Entry()                                  = default;
   _LIBCPP_HIDE_FROM_ABI constexpr _Entry()                         = default;
   _LIBCPP_HIDE_FROM_ABI constexpr _Entry(const _Entry&)            = default;
@@ -153,12 +120,8 @@ class stacktrace_entry {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return native_handle() != 0; }
 
   // (19.6.3.4) [stacktrace.entry.query], query
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string description() const {
-    return __base_.__desc_ ? string(*__base_.__desc_) : string();
-  }
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string source_file() const {
-    return __base_.__file_ ? string(*__base_.__file_) : string();
-  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string description() const { return string(__base_.__desc_.view()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string source_file() const { return string(__base_.__file_.view()); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI uint_least32_t source_line() const { return __base_.__line_; }
 
   // (19.6.3.5) [stacktrace.entry.cmp], comparison
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 b11c50254906c..2ce5ffdc1ccd3 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
@@ -413,6 +413,7 @@
 {'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace14_StringWrapper4viewEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Entry8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Entry9to_stringEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Trace8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
@@ -979,9 +980,8 @@
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace11find_imagesEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace12find_symbolsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace16find_source_locsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace14_StringWrapper6assignENS_17basic_string_viewIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace15populate_imagesEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__stacktrace7_ImagesC1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__stacktrace7_ImagesC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 8b8fde6b28d92..c6923aadb05db 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -627,9 +627,7 @@
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace11find_imagesEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace12find_symbolsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace16find_source_locsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace15populate_imagesEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__stacktrace7_ImagesC1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__stacktrace7_ImagesC2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 6e930022ede4a..41fb1fab1f9e4 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -40,10 +40,12 @@ set(LIBCXX_SOURCES
   ryu/d2fixed.cpp
   ryu/d2s.cpp
   ryu/f2s.cpp
-  stacktrace/images.cpp
-  stacktrace/impl_generic.cpp
-  stacktrace/impl_windows.cpp
-  stacktrace/impl.cpp
+  stacktrace/default_impl.cpp
+  stacktrace/entry.cpp
+  stacktrace/images.h
+  stacktrace/macos_impl.cpp
+  stacktrace/trace.cpp
+  stacktrace/windows_impl.cpp
   stdexcept.cpp
   string.cpp
   support/runtime/exception_fallback.ipp
diff --git a/libcxx/src/stacktrace/images.cpp b/libcxx/src/stacktrace/default_impl.cpp
similarity index 61%
rename from libcxx/src/stacktrace/images.cpp
rename to libcxx/src/stacktrace/default_impl.cpp
index 391d3304523a3..ef6d067a54351 100644
--- a/libcxx/src/stacktrace/images.cpp
+++ b/libcxx/src/stacktrace/default_impl.cpp
@@ -6,50 +6,18 @@
 //
 //===----------------------------------------------------------------------===//
 
-//
-// OS-specific construction
-//
+#if !defined(_WIN32) && !defined(__APPLE__)
 
-#include "__config"
-
-#if defined(__APPLE__)
-// MacOS-specific: use the `dyld` loader to access info about loaded Mach-O images.
-#  include "stacktrace/images.h"
+#  include <__config>
+#  include <__stacktrace/basic_stacktrace.h>
+#  include <__stacktrace/stacktrace_entry.h>
 #  include <algorithm>
-#  include <cstdlib>
-#  include <dlfcn.h>
-#  include <mach-o/dyld.h>
-#  include <mach-o/loader.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-_Images::_Images() {
-  images_[count_++] = {0uz, 0};  // sentinel at low end
-  images_[count_++] = {~0uz, 0}; // sentinel at high end
-  auto dyld_count   = _dyld_image_count();
-  for (unsigned i = 0; i < dyld_count && count_ < k_max_images; i++) {
-    auto& image         = images_[count_++];
-    image.slide_        = uintptr_t(_dyld_get_image_vmaddr_slide(i));
-    image.loaded_at_    = uintptr_t(_dyld_get_image_header(i));
-    image.is_main_prog_ = (i == 0);
-    strncpy(image.name_, _dyld_get_image_name(i), sizeof(image.name_));
-  }
-  std::sort(images_.begin(), images_.begin() + count_);
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#elif !defined(_WIN32)
-// Non-MacOS and non-Windows, including Linux: assume environment has these headers.
-#  include "stacktrace/images.h"
-#  include <algorithm>
-#  include <cstdlib>
 #  include <dlfcn.h>
 #  include <link.h>
 #  include <unistd.h>
 
+#  include "stacktrace/images.h"
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
@@ -86,6 +54,19 @@ _Images::_Images() {
   std::sort(images_.begin(), images_.begin() + count_);
 }
 
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::populate_images() {
+  _Images images;
+  size_t i = 0;
+  for (auto& entry : __entry_iters_()) {
+    images.find(&i, entry.__addr_);
+    if (auto& image = images[i]) {
+      entry.__image_ = ℑ
+      // While we're in this loop, get the executable's path, and tentatively use this for source file.
+      entry.__file_.assign(image.name_);
+    }
+  }
+}
+
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/stacktrace/impl.cpp b/libcxx/src/stacktrace/entry.cpp
similarity index 65%
rename from libcxx/src/stacktrace/impl.cpp
rename to libcxx/src/stacktrace/entry.cpp
index 15f3d3bb86a4c..f29b0df0c655e 100644
--- a/libcxx/src/stacktrace/impl.cpp
+++ b/libcxx/src/stacktrace/entry.cpp
@@ -30,11 +30,11 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& _Entry::write_to(ostream& __os) const {
   constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
 
   __os << "0x" << std::hex << std::setfill('0') << std::setw(__k_addr_width) << __addr_;
-  if (__desc_) {
-    __os << ": " << __desc_->view();
+  if (__desc_.view().size()) {
+    __os << ": " << __desc_.view();
   }
-  if (__file_) {
-    __os << ": " << __file_->view();
+  if (__file_.view().size()) {
+    __os << ": " << __file_.view();
   }
   if (__line_) {
     __os << ":" << std::dec << __line_;
@@ -42,36 +42,12 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& _Entry::write_to(ostream& __os) const {
   return __os;
 }
 
-_LIBCPP_EXPORTED_FROM_ABI ostream& _Trace::write_to(std::ostream& __os) const {
-  auto iters = __entry_iters_();
-  auto count = iters.size();
-  if (!count) {
-    __os << "(empty stacktrace)";
-  } else {
-    for (size_t __i = 0; __i < count; __i++) {
-      // Insert newlines between entries (but not before the first or after the last)
-      if (__i) {
-        __os << '\n';
-      }
-      __os << "  frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": "
-           << *(stacktrace_entry const*)(iters.data() + __i);
-    }
-  }
-  return __os;
-}
-
 _LIBCPP_EXPORTED_FROM_ABI string _Entry::to_string() const {
   stringstream __ss;
   write_to(__ss);
   return __ss.str();
 }
 
-_LIBCPP_EXPORTED_FROM_ABI string _Trace::to_string() const {
-  stringstream __ss;
-  write_to(__ss);
-  return __ss.str();
-}
-
 #endif // _LIBCPP_HAS_LOCALIZATION
 
 _LIBCPP_HIDE_FROM_ABI uintptr_t _Entry::adjusted_addr() const {
diff --git a/libcxx/src/stacktrace/impl_generic.cpp b/libcxx/src/stacktrace/macos_impl.cpp
similarity index 52%
rename from libcxx/src/stacktrace/impl_generic.cpp
rename to libcxx/src/stacktrace/macos_impl.cpp
index 8b8fc66c20e90..431486008dd9b 100644
--- a/libcxx/src/stacktrace/impl_generic.cpp
+++ b/libcxx/src/stacktrace/macos_impl.cpp
@@ -6,21 +6,37 @@
 //
 //===----------------------------------------------------------------------===//
 
-// "Generic" implementation for any platform that doesn't have its own special implementation.
-// (Currently this means any platform other than Windows)
-
-#if !defined(_WIN32)
+#if defined(__APPLE__)
 
 #  include <__config>
 #  include <__stacktrace/basic_stacktrace.h>
 #  include <__stacktrace/stacktrace_entry.h>
+#  include <algorithm>
+#  include <cstdlib>
+#  include <dlfcn.h>
+#  include <mach-o/dyld.h>
+#  include <mach-o/loader.h>
 
 #  include "stacktrace/images.h"
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_images() {
+_Images::_Images() {
+  images_[count_++] = {0uz, 0};  // sentinel at low end
+  images_[count_++] = {~0uz, 0}; // sentinel at high end
+  auto dyld_count   = _dyld_image_count();
+  for (unsigned i = 0; i < dyld_count && count_ < k_max_images; i++) {
+    auto& image         = images_[count_++];
+    image.slide_        = uintptr_t(_dyld_get_image_vmaddr_slide(i));
+    image.loaded_at_    = uintptr_t(_dyld_get_image_header(i));
+    image.is_main_prog_ = (i == 0);
+    strncpy(image.name_, _dyld_get_image_name(i), sizeof(image.name_));
+  }
+  std::sort(images_.begin(), images_.begin() + count_);
+}
+
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::populate_images() {
   _Images images;
   size_t i = 0;
   for (auto& entry : __entry_iters_()) {
@@ -28,19 +44,11 @@ _LIBCPP_EXPORTED_FROM_ABI void _Trace::find_images() {
     if (auto& image = images[i]) {
       entry.__image_ = ℑ
       // While we're in this loop, get the executable's path, and tentatively use this for source file.
-      entry.assign_file(__create_str()).assign(image.name_);
+      entry.__file_.assign(image.name_);
     }
   }
 }
 
-_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_symbols() {
-  // TODO
-}
-
-_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_source_locs() {
-  // TODO
-}
-
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/stacktrace/trace.cpp b/libcxx/src/stacktrace/trace.cpp
new file mode 100644
index 0000000000000..0c50617759ec9
--- /dev/null
+++ b/libcxx/src/stacktrace/trace.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
+#include <string>
+
+#if _LIBCPP_HAS_LOCALIZATION
+#  include <iomanip>
+#  include <iostream>
+#  include <sstream>
+#endif //_LIBCPP_HAS_LOCALIZATION
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __stacktrace {
+
+#if _LIBCPP_HAS_LOCALIZATION
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& _Trace::write_to(std::ostream& __os) const {
+  auto iters = __entry_iters_();
+  auto count = iters.size();
+  if (!count) {
+    __os << "(empty stacktrace)";
+  } else {
+    for (size_t __i = 0; __i < count; __i++) {
+      // Insert newlines between entries (but not before the first or after the last)
+      if (__i) {
+        __os << '\n';
+      }
+      __os << "  frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": "
+           << *(stacktrace_entry const*)(iters.data() + __i);
+    }
+  }
+  return __os;
+}
+
+_LIBCPP_EXPORTED_FROM_ABI string _Trace::to_string() const {
+  stringstream __ss;
+  write_to(__ss);
+  return __ss.str();
+}
+
+#endif // _LIBCPP_HAS_LOCALIZATION
+
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/impl_windows.cpp b/libcxx/src/stacktrace/windows_impl.cpp
similarity index 97%
rename from libcxx/src/stacktrace/impl_windows.cpp
rename to libcxx/src/stacktrace/windows_impl.cpp
index 10258c664d0a1..7653b39557fc6 100644
--- a/libcxx/src/stacktrace/impl_windows.cpp
+++ b/libcxx/src/stacktrace/windows_impl.cpp
@@ -211,7 +211,7 @@ _Trace::windows_impl(size_t skip, size_t max_depth) {
     memset(&mod_info, 0, sizeof(mod_info));
     mod_info.SizeOfStruct = sizeof(mod_info);
     if ((*dbghelp.SymGetModuleInfo64)(proc, frame.AddrPC.Offset, &mod_info)) {
-      entry.assign_file(__create_str()).assign(mod_info.LoadedImageName);
+      entry.__file_.assign(mod_info.LoadedImageName);
     }
 
     --max_depth;
@@ -236,11 +236,11 @@ _Trace::windows_impl(size_t skip, size_t max_depth) {
     DWORD linedisp{0};
     IMAGEHLP_LINE64 line;
     if ((*dbghelp.SymGetSymFromAddr64)(proc, entry.__addr_, &symdisp, sym)) {
-      entry.assign_desc(__create_str()).assign(sym->Name);
+      entry.__desc_.assign(sym->Name);
     }
     line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
     if ((*dbghelp.SymGetLineFromAddr64)(proc, entry.__addr_, &linedisp, &line)) {
-      entry.assign_file(__create_str()).assign(line.FileName);
+      entry.__file_.assign(line.FileName);
       entry.__line_ = line.LineNumber;
     }
   }
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index e33d1f07e3fc3..b2faf316721db 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -938,8 +938,6 @@ stacktrace cwctype
 stacktrace initializer_list
 stacktrace iosfwd
 stacktrace limits
-stacktrace memory
-stacktrace optional
 stacktrace stdexcept
 stacktrace string
 stacktrace string_view
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index c89c6d50b3a4f..01a6b5e9b0039 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -920,8 +920,6 @@ stacktrace cwctype
 stacktrace initializer_list
 stacktrace iosfwd
 stacktrace limits
-stacktrace memory
-stacktrace optional
 stacktrace stdexcept
 stacktrace string
 stacktrace string_view

>From a901e3c1889a20f9e819e793de78d0124ac88ab5 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 27 Oct 2025 17:30:53 -0400
Subject: [PATCH 9/9] Clean up windows impl

---
 .../include/__stacktrace/basic_stacktrace.h   |   8 +-
 libcxx/src/stacktrace/windows_impl.cpp        | 218 ++++++------------
 2 files changed, 80 insertions(+), 146 deletions(-)

diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index dbc6f7f4b0708..e4ae7b297e391 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -131,12 +131,14 @@ class basic_stacktrace : private __stacktrace::_Trace {
   // Creation and assignment [stacktrace.basic.cons]
 
   _LIBCPP_ALWAYS_INLINE // Omit this function from the trace
-  static basic_stacktrace current(const allocator_type& __alloc = allocator_type()) noexcept {
+  static basic_stacktrace
+  current(const allocator_type& __alloc = allocator_type()) noexcept {
     return current(0, __default_max_depth, __alloc);
   }
 
   _LIBCPP_ALWAYS_INLINE // Omit this function from the trace
-  static basic_stacktrace current(size_type __skip, const allocator_type& __alloc = allocator_type()) noexcept {
+  static basic_stacktrace
+  current(size_type __skip, const allocator_type& __alloc = allocator_type()) noexcept {
     return current(__skip, __default_max_depth, __alloc);
   }
 
@@ -148,7 +150,7 @@ class basic_stacktrace : private __stacktrace::_Trace {
     basic_stacktrace __ret{__alloc};
 
     if (__max_depth) {
-#  if defined(WIN32)
+#  if defined(_WIN32)
       __ret.windows_impl(__skip, __max_depth);
 #  else
       __ret.populate_addrs(__skip, __max_depth);
diff --git a/libcxx/src/stacktrace/windows_impl.cpp b/libcxx/src/stacktrace/windows_impl.cpp
index 7653b39557fc6..3af62a8ffe194 100644
--- a/libcxx/src/stacktrace/windows_impl.cpp
+++ b/libcxx/src/stacktrace/windows_impl.cpp
@@ -15,188 +15,124 @@
 #  include <psapi.h>
 //
 #  include <cstring>
-#  include <iostream>
 #  include <mutex>
 #  include <stacktrace>
 
+#  pragma comment(lib, "dbghelp.lib")
+#  pragma comment(lib, "psapi.lib")
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
 namespace {
 
-struct _DLL {
-  HMODULE module_{};
-  bool loaded_{};
-
-  explicit _DLL(char const* name) : module_(LoadLibraryA(name)) {}
-
-  ~_DLL() {
-    if (module_) {
-      FreeLibrary(module_);
-    }
-  }
-
-  template <typename F>
-  bool get_func(F** func, char const* name) {
-    return ((*func = (F*)(void*)GetProcAddress(module_, name)) != nullptr);
-  }
-};
-
-// clang-format off
-
-struct _Dbghelp_DLL final : _DLL {
-  IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
-  bool    (WINAPI *SymCleanup)         (HANDLE);
-  DWORD   (WINAPI *SymGetOptions)      ();
-  bool    (WINAPI *SymGetSearchPath)   (HANDLE, char const*, DWORD);
-  bool    (WINAPI *SymInitialize)      (HANDLE, char const*, bool);
-  DWORD   (WINAPI *SymSetOptions)      (DWORD);
-  bool    (WINAPI *SymSetSearchPath)   (HANDLE, char const*);
-  bool    (WINAPI *StackWalk64)        (DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, void*, void*, void*, void*);
-  void*   (WINAPI *SymFunctionTableAccess64)(HANDLE, DWORD64);
-  bool    (WINAPI *SymGetLineFromAddr64)(HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*);
-  DWORD64 (WINAPI *SymGetModuleBase64) (HANDLE, DWORD64);
-  bool    (WINAPI *SymGetModuleInfo64) (HANDLE, DWORD64, IMAGEHLP_MODULE64*);
-  bool    (WINAPI *SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
-  DWORD64 (WINAPI *SymLoadModule64)    (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
-
-  _Dbghelp_DLL() : _DLL("dbghelp.dll") {
-    loaded_ = true
-      && get_func(&ImageNtHeader, "ImageNtHeader")
-      && get_func(&SymCleanup, "SymCleanup")
-      && get_func(&SymGetOptions, "SymGetOptions")
-      && get_func(&SymGetSearchPath, "SymGetSearchPath")
-      && get_func(&SymInitialize, "SymInitialize")
-      && get_func(&SymSetOptions, "SymSetOptions")
-      && get_func(&SymSetSearchPath, "SymSetSearchPath")
-      && get_func(&StackWalk64, "StackWalk64")
-      && get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")
-      && get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")
-      && get_func(&SymGetModuleBase64, "SymGetModuleBase64")
-      && get_func(&SymGetModuleInfo64, "SymGetModuleInfo64")
-      && get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")
-      && get_func(&SymLoadModule64, "SymLoadModule64")
-      ;
-  }
-};
-
-struct _Psapi_DLL final : _DLL {
-  bool  (WINAPI *EnumProcessModules)   (HANDLE, HMODULE*, DWORD, DWORD*);
-  bool  (WINAPI *GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
-  DWORD (WINAPI *GetModuleBaseName)    (HANDLE, HMODULE, char**, DWORD);
-
-  _Psapi_DLL() : _DLL("psapi.dll") {
-    loaded_ = true
-      && get_func(&EnumProcessModules, "EnumProcessModules")
-      && get_func(&GetModuleInformation, "GetModuleInformation")
-      && get_func(&GetModuleBaseName, "GetModuleBaseNameA")
-    ;
-  }
-};
-
 struct _Sym_Init_Scope {
-  _Dbghelp_DLL& dbghelp_;
   HANDLE proc_;
 
-  _Sym_Init_Scope(_Dbghelp_DLL& dbghelp, HANDLE proc)
-    : dbghelp_(dbghelp), proc_(proc) {
-    (*dbghelp_.SymInitialize)(proc_, nullptr, true);
-  }
-  ~_Sym_Init_Scope() {
-    (*dbghelp_.SymCleanup)(proc_);
-  }
+  explicit _Sym_Init_Scope(HANDLE proc) : proc_(proc) { SymInitialize(proc_, nullptr, true); }
+  ~_Sym_Init_Scope() { SymCleanup(proc_); }
 };
 
-}  // namespace
+} // namespace
 
-_LIBCPP_EXPORTED_FROM_ABI void
-_Trace::windows_impl(size_t skip, size_t max_depth) {
-  if (!max_depth) [[unlikely]] {
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::windows_impl(size_t skip, size_t max_depth) {
+  if (!max_depth) {
     return;
   }
 
-  static _Psapi_DLL psapi;
-  static _Dbghelp_DLL dbghelp;
-  if (!psapi.loaded_ || !dbghelp.loaded_) { return; }
+  // Use the Windows Debug Help and Process Status libraries to get a stacktrace.
+  //   https://learn.microsoft.com/en-us/windows/win32/debug/debug-help-library
+  //   https://learn.microsoft.com/en-us/windows/win32/psapi/process-status-helper
 
-  // Not thread-safe according to docs
+  // These APIs are not thread-safe, according to docs.
   static std::mutex api_mutex;
   std::lock_guard<std::mutex> api_guard(api_mutex);
 
   HANDLE proc = GetCurrentProcess();
   HMODULE exe = GetModuleHandle(nullptr);
-  if (!exe) { return; }
+  if (!exe) {
+    return;
+  }
 
-  _Sym_Init_Scope symscope(dbghelp, proc);
+  _Sym_Init_Scope symscope(proc);
 
-  char sym_path[MAX_PATH * 4]; // arbitrary
-  if (!(*dbghelp.SymGetSearchPath)(proc, sym_path, sizeof(sym_path))) { return; }
+  // Allow space for a handful of paths
+  char sym_path[MAX_PATH * 4];
+  if (!SymGetSearchPath(proc, sym_path, sizeof(sym_path))) {
+    return;
+  }
 
   char exe_dir[MAX_PATH];
-  if (!GetModuleFileNameA(nullptr, exe_dir, sizeof(exe_dir))) { return; }
+  if (!GetModuleFileNameA(nullptr, exe_dir, sizeof(exe_dir))) {
+    return;
+  }
   size_t exe_dir_len = strlen(exe_dir);
-  while (exe_dir_len > 0 && exe_dir[exe_dir_len - 1] != '\\') { exe_dir[--exe_dir_len] = 0; }
-  if (exe_dir_len > 0) { exe_dir[--exe_dir_len] = 0; }  // strip last backslash
+  while (exe_dir_len > 0 && exe_dir[exe_dir_len - 1] != '\\') {
+    exe_dir[--exe_dir_len] = 0;
+  }
+  if (exe_dir_len > 0) {
+    exe_dir[--exe_dir_len] = 0;
+  } // strip last backslash
 
   if (!strstr(sym_path, exe_dir)) {
-    (void) strncat(sym_path, ";", sizeof(sym_path) - 1);
-    (void) strncat(sym_path, exe_dir, sizeof(sym_path) - 1);
-    if (!(*dbghelp.SymSetSearchPath)(proc, sym_path)) { return; }
+    (void)strncat(sym_path, ";", sizeof(sym_path) - 1);
+    (void)strncat(sym_path, exe_dir, sizeof(sym_path) - 1);
+    if (!SymSetSearchPath(proc, sym_path)) {
+      return;
+    }
   }
 
   IMAGE_NT_HEADERS* nt_headers;
-  if (!(nt_headers = (*dbghelp.ImageNtHeader)(exe))) { return; }
+  if (!(nt_headers = ImageNtHeader(exe))) {
+    return;
+  }
 
-  (*dbghelp.SymSetOptions)(
-    (*dbghelp.SymGetOptions)()
-    | SYMOPT_LOAD_LINES
-    | SYMOPT_UNDNAME);
+  SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
 
-  auto thread  = GetCurrentThread();
-  auto machine = nt_headers->FileHeader.Machine;
+  HANDLE thread = GetCurrentThread();
+  int machine   = nt_headers->FileHeader.Machine;
 
   CONTEXT ccx;
   RtlCaptureContext(&ccx);
 
-  STACKFRAME64 frame;
+  STACKFRAME frame;
   memset(&frame, 0, sizeof(frame));
-  frame.AddrPC.Mode      = AddrModeFlat;
-  frame.AddrStack.Mode   = AddrModeFlat;
-  frame.AddrFrame.Mode   = AddrModeFlat;
-#if defined(_M_AMD64)
+  frame.AddrPC.Mode    = AddrModeFlat;
+  frame.AddrStack.Mode = AddrModeFlat;
+  frame.AddrFrame.Mode = AddrModeFlat;
+#  if defined(_M_AMD64)
   frame.AddrPC.Offset    = ccx.Rip;
   frame.AddrStack.Offset = ccx.Rsp;
   frame.AddrFrame.Offset = ccx.Rbp;
-#elif defined(_M_ARM64)
+#  elif defined(_M_ARM64)
   frame.AddrPC.Offset    = ccx.Pc;
   frame.AddrStack.Offset = ccx.Sp;
   frame.AddrFrame.Offset = ccx.Fp;
-#elif defined(_M_IX86)
+#  elif defined(_M_IX86)
   frame.AddrPC.Offset    = ccx.Eip;
   frame.AddrStack.Offset = ccx.Esp;
   frame.AddrFrame.Offset = ccx.Ebp;
-#else
-# warning unrecognized architecture; returned stacktraces will be empty
+#  else
+#    warning unrecognized architecture; returned stacktraces will be empty
   return;
-#endif
+#  endif
 
   // Skip call to this `current_impl` func
   ++skip;
-  
-  while (max_depth) {
 
-    if (!(*dbghelp.StackWalk64)(
-          machine, proc, thread, &frame, &ccx, nullptr,
-          (void*) dbghelp.SymFunctionTableAccess64,
-          (void*) dbghelp.SymGetModuleBase64,
-          nullptr)) {
+  while (max_depth) {
+    if (!StackWalk(machine, proc, thread, &frame, &ccx, nullptr, SymFunctionTableAccess, SymGetModuleBase, nullptr)) {
       break;
     }
 
-    if (skip && skip--) { continue; }
-    if (!frame.AddrPC.Offset) { break; }
+    if (skip && skip--) {
+      continue;
+    }
+    if (!frame.AddrPC.Offset) {
+      break;
+    }
 
-    auto& entry = this->__entry_append_();
+    _Entry& entry = this->__entry_append_();
 
     // Note: can't differentiate between a signal / exception, or a normal function call.
     // This assumes the more common (presumably) case of normal function calls, so we'll
@@ -207,10 +143,10 @@ _Trace::windows_impl(size_t skip, size_t max_depth) {
     // itself or a DLL.  This is used in place of the source filename, if the source filename
     // cannot be found (missing PDB, etc.).  If the source file can be determined this will
     // be overwritten.
-    IMAGEHLP_MODULE64 mod_info;
+    IMAGEHLP_MODULE mod_info;
     memset(&mod_info, 0, sizeof(mod_info));
     mod_info.SizeOfStruct = sizeof(mod_info);
-    if ((*dbghelp.SymGetModuleInfo64)(proc, frame.AddrPC.Offset, &mod_info)) {
+    if (SymGetModuleInfo(proc, frame.AddrPC.Offset, &mod_info)) {
       entry.__file_.assign(mod_info.LoadedImageName);
     }
 
@@ -218,28 +154,24 @@ _Trace::windows_impl(size_t skip, size_t max_depth) {
   }
 
   DWORD need_bytes = 0;
-  HMODULE module_handles[1024] {0};
-  if (!(*psapi.EnumProcessModules)(
-          proc, module_handles, sizeof(module_handles), LPDWORD(&need_bytes))) {
+  HMODULE module_handles[1024]{0};
+  if (!EnumProcessModules(proc, module_handles, sizeof(module_handles), LPDWORD(&need_bytes))) {
     return;
   }
 
-  // Symbols longer than this will be truncated.
-  static constexpr size_t kMaxSymName = 256;
-
-  for (auto& entry : __entry_iters_()) {
-    char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName + 1];
-    auto* sym          = (IMAGEHLP_SYMBOL64*)space;
-    sym->SizeOfStruct  = sizeof(IMAGEHLP_SYMBOL64);
-    sym->MaxNameLength = kMaxSymName;
-    uint64_t symdisp{0};
+  for (_Entry& entry : __entry_iters_()) {
+    char space[sizeof(IMAGEHLP_SYMBOL) + _Entry::__max_sym_len + 1];
+    IMAGEHLP_SYMBOL* sym = reinterpret_cast<IMAGEHLP_SYMBOL*>(space);
+    sym->SizeOfStruct    = sizeof(IMAGEHLP_SYMBOL);
+    sym->MaxNameLength   = _Entry::__max_sym_len;
+    uintptr_t symdisp{0};
     DWORD linedisp{0};
-    IMAGEHLP_LINE64 line;
-    if ((*dbghelp.SymGetSymFromAddr64)(proc, entry.__addr_, &symdisp, sym)) {
+    IMAGEHLP_LINE line;
+    if (SymGetSymFromAddr(proc, entry.__addr_, &symdisp, sym)) {
       entry.__desc_.assign(sym->Name);
     }
-    line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
-    if ((*dbghelp.SymGetLineFromAddr64)(proc, entry.__addr_, &linedisp, &line)) {
+    line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
+    if (SymGetLineFromAddr(proc, entry.__addr_, &linedisp, &line)) {
       entry.__file_.assign(line.FileName);
       entry.__line_ = line.LineNumber;
     }
@@ -249,4 +181,4 @@ _Trace::windows_impl(size_t skip, size_t max_depth) {
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif  // _WIN32
+#endif // _WIN32



More information about the libcxx-commits mailing list