Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 25 Apr 2026 14:20:21 +0000
From:      Dimitry Andric <dim@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 4fc1fdd206c3 - main - Merge commit c1d26c3c2510 from llvm git (by Nikolas Klauser):
Message-ID:  <69eccda5.3774c.179ff3ae@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by dim:

URL: https://cgit.FreeBSD.org/src/commit/?id=4fc1fdd206c3db60ca521e3966f31044c7711398

commit 4fc1fdd206c3db60ca521e3966f31044c7711398
Author:     Dimitry Andric <dim@FreeBSD.org>
AuthorDate: 2026-03-12 13:24:01 +0000
Commit:     Dimitry Andric <dim@FreeBSD.org>
CommitDate: 2026-04-25 14:14:22 +0000

    Merge commit c1d26c3c2510 from llvm git (by Nikolas Klauser):
    
      [libc++] Fix iostream size ABI break (#185839)
    
      In #124103 we changed the size of various iostream objects, which turns
      out to be ABI breaking when compiling non-PIE code.
    
      This ABI break is safe to fix, since for any programs allocating more
      memory for the iostream objects, the remaining bytes are simply unused
      now.
    
      Fixes #185724
    
    This fixes the ABI break that causes programs that use the standard
    streams to terminate in various interesting ways, usually by throwing an
    unexpected std::bad_cast exception.
    
    PR:             292067
    MFC after:      1 month
---
 contrib/llvm-project/libcxx/src/iostream.cpp | 93 +++++++++++++++++-----------
 1 file changed, 56 insertions(+), 37 deletions(-)

diff --git a/contrib/llvm-project/libcxx/src/iostream.cpp b/contrib/llvm-project/libcxx/src/iostream.cpp
index 416725235c34..b216c6ad35da 100644
--- a/contrib/llvm-project/libcxx/src/iostream.cpp
+++ b/contrib/llvm-project/libcxx/src/iostream.cpp
@@ -16,24 +16,40 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+// This file implements the various stream objects provided inside <iostream>. We're doing some ODR violations in here,
+// so this quite fragile. Specifically, the size of the stream objects (i.e. cout, cin etc.) needs to stay the same.
+// For that reason, we have `stream` and `stream_data` separated into two objects. The public `stream` objects only
+// contain the actual stream, while the private `stream_data` objects contains the `basic_streambuf` we're using as well
+// as the mbstate_t. `stream_data` objects are only accessible within the library, so they aren't ABI sensitive and we
+// can change them as we want.
+
+template <class StreamT>
+union stream {
+  constexpr stream() {}
+  stream(const stream&)            = delete;
+  stream& operator=(const stream&) = delete;
+  constexpr ~stream() {}
+
+  StreamT value;
+};
+
 template <class StreamT, class BufferT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    // The stream has to be the first element, since that's referenced by the stream declarations in <iostream>
-    StreamT stream;
     BufferT buffer;
     mbstate_t mb;
   };
-
-  void init(FILE* stdstream) {
-    mb = {};
-    std::construct_at(&buffer, stdstream, &mb);
-    std::construct_at(&stream, &buffer);
-  }
 };
 
+template <class StreamT, class BufferT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+  data.mb = {};
+  std::construct_at(&data.buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.buffer);
+}
+
 #define CHAR_MANGLING_char "D"
 #define CHAR_MANGLING_wchar_t "_W"
 #define CHAR_MANGLING(CharT) CHAR_MANGLING_##CharT
@@ -46,25 +62,28 @@ union stream_data {
 
 #ifdef _LIBCPP_ABI_MICROSOFT
 #  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var __asm__(                                     \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+    _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12@A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var) STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var
+#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+    _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_istream, __stdinbuf, char, cin);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, cout);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, cerr);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, clog);
+STREAM(basic_istream, __stdinbuf, char, cin);
+STREAM(basic_ostream, __stdoutbuf, char, cout);
+STREAM(basic_ostream, __stdoutbuf, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -98,34 +117,34 @@ public:
 DoIOSInit::DoIOSInit() {
   force_locale_initialization();
 
-  cin.init(stdin);
-  cout.init(stdout);
-  cerr.init(stderr);
-  clog.init(stderr);
+  init_stream(stdin, cin, cin_data);
+  init_stream(stdout, cout, cout_data);
+  init_stream(stderr, cerr, cerr_data);
+  init_stream(stderr, clog, clog_data);
 
-  cin.stream.tie(&cout.stream);
-  std::unitbuf(cerr.stream);
-  cerr.stream.tie(&cout.stream);
+  cin.value.tie(&cout.value);
+  std::unitbuf(cerr.value);
+  cerr.value.tie(&cout.value);
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-  wcin.init(stdin);
-  wcout.init(stdout);
-  wcerr.init(stderr);
-  wclog.init(stderr);
-
-  wcin.stream.tie(&wcout.stream);
-  std::unitbuf(wcerr.stream);
-  wcerr.stream.tie(&wcout.stream);
+  init_stream(stdin, wcin, wcin_data);
+  init_stream(stdout, wcout, wcout_data);
+  init_stream(stderr, wcerr, wcerr_data);
+  init_stream(stderr, wclog, wclog_data);
+
+  wcin.value.tie(&wcout.value);
+  std::unitbuf(wcerr.value);
+  wcerr.value.tie(&wcout.value);
 #endif
 }
 
 DoIOSInit::~DoIOSInit() {
-  cout.stream.flush();
-  clog.stream.flush();
+  cout.value.flush();
+  clog.value.flush();
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-  wcout.stream.flush();
-  wclog.stream.flush();
+  wcout.value.flush();
+  wclog.value.flush();
 #endif
 }
 


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69eccda5.3774c.179ff3ae>