diff mbox series

[2/2] libstdc++: Move stream initialization into compiled library [PR44952]

Message ID 20221104150525.2968778-2-ppalka@redhat.com
State New
Headers show
Series [1/2] c++: correct __has_attribute(init_priority) | expand

Commit Message

Patrick Palka Nov. 4, 2022, 3:05 p.m. UTC
This patch moves the global object for constructing the standard streams
out from <iostream> and into the compiled library on targets that support
the init_priority attribute.  This means that <iostream> no longer
introduces a separate global constructor in each TU that includes it.

We can do this only if the init_priority attribute is supported because
we need to force that the stream initialization runs first before any
user-defined global initializer in programs that that use a static
libstdc++.a.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look right?
Unfortunately I don't have access to a system that truly doesn't support
init priorities, so I instead tested that situation by artificially
disabling the init_priority attribute on x86_64.

	PR libstdc++/44952
	PR libstdc++/98108

libstdc++-v3/ChangeLog:

	* include/bits/c++config (_GLIBCXX_HAS_ATTRIBUTE): Define.
	(_GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY): Define.
	* include/std/iostream (__ioinit): Define only if init_priority
	attribute isn't usable.
	* src/c++98/ios_init.cc (__ioinit): Define here instead if
	the init_priority is usable.
	* src/c++98/ios_base_init.h: New file.
---
 libstdc++-v3/include/bits/c++config    | 12 ++++++++++++
 libstdc++-v3/include/std/iostream      |  4 ++++
 libstdc++-v3/src/c++98/ios_base_init.h |  9 +++++++++
 libstdc++-v3/src/c++98/ios_init.cc     |  4 ++++
 4 files changed, 29 insertions(+)
 create mode 100644 libstdc++-v3/src/c++98/ios_base_init.h

Comments

Jonathan Wakely Nov. 4, 2022, 3:24 p.m. UTC | #1
On Fri, 4 Nov 2022 at 15:08, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> This patch moves the global object for constructing the standard streams
> out from <iostream> and into the compiled library on targets that support
> the init_priority attribute.  This means that <iostream> no longer
> introduces a separate global constructor in each TU that includes it.
>
> We can do this only if the init_priority attribute is supported because
> we need to force that the stream initialization runs first before any
> user-defined global initializer in programs that that use a static
> libstdc++.a.
>
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look right?
> Unfortunately I don't have access to a system that truly doesn't support
> init priorities, so I instead tested that situation by artificially
> disabling the init_priority attribute on x86_64.
>
>         PR libstdc++/44952
>         PR libstdc++/98108
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/c++config (_GLIBCXX_HAS_ATTRIBUTE): Define.
>         (_GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY): Define.
>         * include/std/iostream (__ioinit): Define only if init_priority
>         attribute isn't usable.
>         * src/c++98/ios_init.cc (__ioinit): Define here instead if
>         the init_priority is usable.
>         * src/c++98/ios_base_init.h: New file.
> ---
>  libstdc++-v3/include/bits/c++config    | 12 ++++++++++++
>  libstdc++-v3/include/std/iostream      |  4 ++++
>  libstdc++-v3/src/c++98/ios_base_init.h |  9 +++++++++
>  libstdc++-v3/src/c++98/ios_init.cc     |  4 ++++
>  4 files changed, 29 insertions(+)
>  create mode 100644 libstdc++-v3/src/c++98/ios_base_init.h
>
> diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config
> index 50406066afe..f93076191d9 100644
> --- a/libstdc++-v3/include/bits/c++config
> +++ b/libstdc++-v3/include/bits/c++config
> @@ -837,6 +837,18 @@ namespace __gnu_cxx
>
>  #undef _GLIBCXX_HAS_BUILTIN
>
> +#ifdef __has_attribute

Do we need this?
I think all the compilers we support implemented this long ago (clang
had it before gcc, and Intel has it for gcc compat, and any others had
better have it by now too).

So we can just use #if __has_attribute directly, instead of defining
these extra macros.

> +# define _GLIBCXX_HAS_ATTRIBUTE(B) __has_attribute(B)
> +#else
> +# define _GLIBCXX_HAS_ATTRIBUTE(B) 0
> +#endif
> +
> +#if _GLIBCXX_HAS_ATTRIBUTE(init_priority)
> +# define _GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY 1
> +#endif
> +
> +#undef _GLIBCXX_HAS_ATTRIBUTE
> +
>  // Mark code that should be ignored by the compiler, but seen by Doxygen.
>  #define _GLIBCXX_DOXYGEN_ONLY(X)
>
> diff --git a/libstdc++-v3/include/std/iostream b/libstdc++-v3/include/std/iostream
> index 70318a45891..5eaa9755d9a 100644
> --- a/libstdc++-v3/include/std/iostream
> +++ b/libstdc++-v3/include/std/iostream
> @@ -73,7 +73,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    ///@}
>
>    // For construction of filebuffers for cout, cin, cerr, clog et. al.
> +  // When the init_priority attribute is usable, we do this initialization
> +  // in the compiled library instead (see src/c++98/ios_init.cc).
> +#if !_GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY
>    static ios_base::Init __ioinit;
> +#endif
>
>  _GLIBCXX_END_NAMESPACE_VERSION
>  } // namespace
> diff --git a/libstdc++-v3/src/c++98/ios_base_init.h b/libstdc++-v3/src/c++98/ios_base_init.h
> new file mode 100644
> index 00000000000..f3087d1da3c
> --- /dev/null
> +++ b/libstdc++-v3/src/c++98/ios_base_init.h
> @@ -0,0 +1,9 @@
> +// This is only in a header so we can use the system_header pragma,
> +// to suppress the warning caused by using a reserved init_priority.
> +#pragma GCC system_header

Ugh, that's annoying.

> +
> +#if !_GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY
> +# error "This file should not be included for this build"
> +#endif
> +
> +static ios_base::Init __ioinit __attribute__((init_priority(90)));
> diff --git a/libstdc++-v3/src/c++98/ios_init.cc b/libstdc++-v3/src/c++98/ios_init.cc
> index 1b5132f1c2d..954fa9f29cf 100644
> --- a/libstdc++-v3/src/c++98/ios_init.cc
> +++ b/libstdc++-v3/src/c++98/ios_init.cc
> @@ -75,6 +75,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    extern wostream wclog;
>  #endif
>
> +#if _GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY
> +# include "ios_base_init.h"
> +#endif
> +
>    ios_base::Init::Init()
>    {
>      if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, 1) == 0)
> --
> 2.38.1.385.g3b08839926
>
Patrick Palka Nov. 4, 2022, 4:31 p.m. UTC | #2
On Fri, 4 Nov 2022, Jonathan Wakely wrote:

> On Fri, 4 Nov 2022 at 15:08, Patrick Palka via Libstdc++
> <libstdc++@gcc.gnu.org> wrote:
> >
> > This patch moves the global object for constructing the standard streams
> > out from <iostream> and into the compiled library on targets that support
> > the init_priority attribute.  This means that <iostream> no longer
> > introduces a separate global constructor in each TU that includes it.
> >
> > We can do this only if the init_priority attribute is supported because
> > we need to force that the stream initialization runs first before any
> > user-defined global initializer in programs that that use a static
> > libstdc++.a.
> >
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look right?
> > Unfortunately I don't have access to a system that truly doesn't support
> > init priorities, so I instead tested that situation by artificially
> > disabling the init_priority attribute on x86_64.
> >
> >         PR libstdc++/44952
> >         PR libstdc++/98108
> >
> > libstdc++-v3/ChangeLog:
> >
> >         * include/bits/c++config (_GLIBCXX_HAS_ATTRIBUTE): Define.
> >         (_GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY): Define.
> >         * include/std/iostream (__ioinit): Define only if init_priority
> >         attribute isn't usable.
> >         * src/c++98/ios_init.cc (__ioinit): Define here instead if
> >         the init_priority is usable.
> >         * src/c++98/ios_base_init.h: New file.
> > ---
> >  libstdc++-v3/include/bits/c++config    | 12 ++++++++++++
> >  libstdc++-v3/include/std/iostream      |  4 ++++
> >  libstdc++-v3/src/c++98/ios_base_init.h |  9 +++++++++
> >  libstdc++-v3/src/c++98/ios_init.cc     |  4 ++++
> >  4 files changed, 29 insertions(+)
> >  create mode 100644 libstdc++-v3/src/c++98/ios_base_init.h
> >
> > diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config
> > index 50406066afe..f93076191d9 100644
> > --- a/libstdc++-v3/include/bits/c++config
> > +++ b/libstdc++-v3/include/bits/c++config
> > @@ -837,6 +837,18 @@ namespace __gnu_cxx
> >
> >  #undef _GLIBCXX_HAS_BUILTIN
> >
> > +#ifdef __has_attribute
> 
> Do we need this?
> I think all the compilers we support implemented this long ago (clang
> had it before gcc, and Intel has it for gcc compat, and any others had
> better have it by now too).
> 
> So we can just use #if __has_attribute directly, instead of defining
> these extra macros.

Sounds good, I wasn't sure if we can assume support for __has_attribute
or not.

> 
> > +# define _GLIBCXX_HAS_ATTRIBUTE(B) __has_attribute(B)
> > +#else
> > +# define _GLIBCXX_HAS_ATTRIBUTE(B) 0
> > +#endif
> > +
> > +#if _GLIBCXX_HAS_ATTRIBUTE(init_priority)
> > +# define _GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY 1
> > +#endif
> > +
> > +#undef _GLIBCXX_HAS_ATTRIBUTE
> > +
> >  // Mark code that should be ignored by the compiler, but seen by Doxygen.
> >  #define _GLIBCXX_DOXYGEN_ONLY(X)
> >
> > diff --git a/libstdc++-v3/include/std/iostream b/libstdc++-v3/include/std/iostream
> > index 70318a45891..5eaa9755d9a 100644
> > --- a/libstdc++-v3/include/std/iostream
> > +++ b/libstdc++-v3/include/std/iostream
> > @@ -73,7 +73,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >    ///@}
> >
> >    // For construction of filebuffers for cout, cin, cerr, clog et. al.
> > +  // When the init_priority attribute is usable, we do this initialization
> > +  // in the compiled library instead (see src/c++98/ios_init.cc).
> > +#if !_GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY
> >    static ios_base::Init __ioinit;
> > +#endif
> >
> >  _GLIBCXX_END_NAMESPACE_VERSION
> >  } // namespace
> > diff --git a/libstdc++-v3/src/c++98/ios_base_init.h b/libstdc++-v3/src/c++98/ios_base_init.h
> > new file mode 100644
> > index 00000000000..f3087d1da3c
> > --- /dev/null
> > +++ b/libstdc++-v3/src/c++98/ios_base_init.h
> > @@ -0,0 +1,9 @@
> > +// This is only in a header so we can use the system_header pragma,
> > +// to suppress the warning caused by using a reserved init_priority.
> > +#pragma GCC system_header
> 
> Ugh, that's annoying.

Yeah :( we employ the same workaround in
src/c++17/{memory_resource.cc,default_resource.h} too.

Here's v2 which gets rid of the new macros, adds more comments and other
minor improvements.  Only smoke tested so far, full bootstrap and
regtesting in progress:

-- >8 --

Subject: [PATCH 2/2] libstdc++: Move stream initialization into compiled
 library [PR44952]

This patch moves the global object for constructing the standard streams
out from <iostream> and into the compiled library on targets that support
init priorities.  This means that <iostream> no longer introduces a
separate global constructor in each TU that includes it.

We can do this only if the init_priority attribute is supported because
we need a way to force the stream initialization to run first before any
user-defined global initializer, particularly for programs that use a
static libstdc++.a.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look right?
Unfortunately I don't have access to a system that truly doesn't support
init priorities, so I instead tested that situation by artificially
disabling the init_priority attribute on x86_64.

	PR libstdc++/44952
	PR libstdc++/98108

libstdc++-v3/ChangeLog:

	* include/std/iostream (__ioinit): No longer define here if
	init_priority attribute is usable.
	* src/c++98/ios_init.cc (__ioinit): Define here instead if
	the init_priority is usable.
	* src/c++98/ios_base_init.h: New file.
---
 libstdc++-v3/include/std/iostream      |  4 ++++
 libstdc++-v3/src/c++98/ios_base_init.h | 12 ++++++++++++
 libstdc++-v3/src/c++98/ios_init.cc     |  2 ++
 3 files changed, 18 insertions(+)
 create mode 100644 libstdc++-v3/src/c++98/ios_base_init.h

diff --git a/libstdc++-v3/include/std/iostream b/libstdc++-v3/include/std/iostream
index 70318a45891..ff78e1cfb87 100644
--- a/libstdc++-v3/include/std/iostream
+++ b/libstdc++-v3/include/std/iostream
@@ -73,7 +73,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   ///@}
 
   // For construction of filebuffers for cout, cin, cerr, clog et. al.
+  // When the init_priority attribute is usable, we do this initialization
+  // in the compiled library instead (src/c++98/ios_init.cc).
+#if !__has_attribute(__init_priority__)
   static ios_base::Init __ioinit;
+#endif
 
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
diff --git a/libstdc++-v3/src/c++98/ios_base_init.h b/libstdc++-v3/src/c++98/ios_base_init.h
new file mode 100644
index 00000000000..fe45e0e98a9
--- /dev/null
+++ b/libstdc++-v3/src/c++98/ios_base_init.h
@@ -0,0 +1,12 @@
+// This is only in a header so we can use the system_header pragma,
+// to suppress the warning caused by using a reserved init_priority.
+#pragma GCC system_header
+
+// If the target supports init priorities, set up a static object in the
+// compiled library to perform the <iostream> initialization once and
+// sufficiently early (so that the initialization runs first before any
+// other global constructor when staticaly linking with libstdc++.a),
+// instead of having to do so in each TU that includes <iostream>.
+#if __has_attribute(init_priority)
+static ios_base::Init __ioinit __attribute__((init_priority(90)));
+#endif
diff --git a/libstdc++-v3/src/c++98/ios_init.cc b/libstdc++-v3/src/c++98/ios_init.cc
index 1b5132f1c2d..4016fcab785 100644
--- a/libstdc++-v3/src/c++98/ios_init.cc
+++ b/libstdc++-v3/src/c++98/ios_init.cc
@@ -75,6 +75,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   extern wostream wclog;
 #endif
 
+#include "ios_base_init.h"
+
   ios_base::Init::Init()
   {
     if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, 1) == 0)
Jonathan Wakely Nov. 4, 2022, 4:42 p.m. UTC | #3
On Fri, 4 Nov 2022 at 16:31, Patrick Palka <ppalka@redhat.com> wrote:
>
> On Fri, 4 Nov 2022, Jonathan Wakely wrote:
>
> > On Fri, 4 Nov 2022 at 15:08, Patrick Palka via Libstdc++
> > <libstdc++@gcc.gnu.org> wrote:
> > >
> > > This patch moves the global object for constructing the standard streams
> > > out from <iostream> and into the compiled library on targets that support
> > > the init_priority attribute.  This means that <iostream> no longer
> > > introduces a separate global constructor in each TU that includes it.
> > >
> > > We can do this only if the init_priority attribute is supported because
> > > we need to force that the stream initialization runs first before any
> > > user-defined global initializer in programs that that use a static
> > > libstdc++.a.
> > >
> > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look right?
> > > Unfortunately I don't have access to a system that truly doesn't support
> > > init priorities, so I instead tested that situation by artificially
> > > disabling the init_priority attribute on x86_64.
> > >
> > >         PR libstdc++/44952
> > >         PR libstdc++/98108
> > >
> > > libstdc++-v3/ChangeLog:
> > >
> > >         * include/bits/c++config (_GLIBCXX_HAS_ATTRIBUTE): Define.
> > >         (_GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY): Define.
> > >         * include/std/iostream (__ioinit): Define only if init_priority
> > >         attribute isn't usable.
> > >         * src/c++98/ios_init.cc (__ioinit): Define here instead if
> > >         the init_priority is usable.
> > >         * src/c++98/ios_base_init.h: New file.
> > > ---
> > >  libstdc++-v3/include/bits/c++config    | 12 ++++++++++++
> > >  libstdc++-v3/include/std/iostream      |  4 ++++
> > >  libstdc++-v3/src/c++98/ios_base_init.h |  9 +++++++++
> > >  libstdc++-v3/src/c++98/ios_init.cc     |  4 ++++
> > >  4 files changed, 29 insertions(+)
> > >  create mode 100644 libstdc++-v3/src/c++98/ios_base_init.h
> > >
> > > diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config
> > > index 50406066afe..f93076191d9 100644
> > > --- a/libstdc++-v3/include/bits/c++config
> > > +++ b/libstdc++-v3/include/bits/c++config
> > > @@ -837,6 +837,18 @@ namespace __gnu_cxx
> > >
> > >  #undef _GLIBCXX_HAS_BUILTIN
> > >
> > > +#ifdef __has_attribute
> >
> > Do we need this?
> > I think all the compilers we support implemented this long ago (clang
> > had it before gcc, and Intel has it for gcc compat, and any others had
> > better have it by now too).
> >
> > So we can just use #if __has_attribute directly, instead of defining
> > these extra macros.
>
> Sounds good, I wasn't sure if we can assume support for __has_attribute
> or not.
>
> >
> > > +# define _GLIBCXX_HAS_ATTRIBUTE(B) __has_attribute(B)
> > > +#else
> > > +# define _GLIBCXX_HAS_ATTRIBUTE(B) 0
> > > +#endif
> > > +
> > > +#if _GLIBCXX_HAS_ATTRIBUTE(init_priority)
> > > +# define _GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY 1
> > > +#endif
> > > +
> > > +#undef _GLIBCXX_HAS_ATTRIBUTE
> > > +
> > >  // Mark code that should be ignored by the compiler, but seen by Doxygen.
> > >  #define _GLIBCXX_DOXYGEN_ONLY(X)
> > >
> > > diff --git a/libstdc++-v3/include/std/iostream b/libstdc++-v3/include/std/iostream
> > > index 70318a45891..5eaa9755d9a 100644
> > > --- a/libstdc++-v3/include/std/iostream
> > > +++ b/libstdc++-v3/include/std/iostream
> > > @@ -73,7 +73,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > >    ///@}
> > >
> > >    // For construction of filebuffers for cout, cin, cerr, clog et. al.
> > > +  // When the init_priority attribute is usable, we do this initialization
> > > +  // in the compiled library instead (see src/c++98/ios_init.cc).
> > > +#if !_GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY
> > >    static ios_base::Init __ioinit;
> > > +#endif
> > >
> > >  _GLIBCXX_END_NAMESPACE_VERSION
> > >  } // namespace
> > > diff --git a/libstdc++-v3/src/c++98/ios_base_init.h b/libstdc++-v3/src/c++98/ios_base_init.h
> > > new file mode 100644
> > > index 00000000000..f3087d1da3c
> > > --- /dev/null
> > > +++ b/libstdc++-v3/src/c++98/ios_base_init.h
> > > @@ -0,0 +1,9 @@
> > > +// This is only in a header so we can use the system_header pragma,
> > > +// to suppress the warning caused by using a reserved init_priority.
> > > +#pragma GCC system_header
> >
> > Ugh, that's annoying.
>
> Yeah :( we employ the same workaround in
> src/c++17/{memory_resource.cc,default_resource.h} too.
>
> Here's v2 which gets rid of the new macros, adds more comments and other
> minor improvements.  Only smoke tested so far, full bootstrap and
> regtesting in progress:

OK for trunk assuming testing passes.

Thanks for working on it.


>
> -- >8 --
>
> Subject: [PATCH 2/2] libstdc++: Move stream initialization into compiled
>  library [PR44952]
>
> This patch moves the global object for constructing the standard streams
> out from <iostream> and into the compiled library on targets that support
> init priorities.  This means that <iostream> no longer introduces a
> separate global constructor in each TU that includes it.
>
> We can do this only if the init_priority attribute is supported because
> we need a way to force the stream initialization to run first before any
> user-defined global initializer, particularly for programs that use a
> static libstdc++.a.
>
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look right?
> Unfortunately I don't have access to a system that truly doesn't support
> init priorities, so I instead tested that situation by artificially
> disabling the init_priority attribute on x86_64.
>
>         PR libstdc++/44952
>         PR libstdc++/98108
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/iostream (__ioinit): No longer define here if
>         init_priority attribute is usable.
>         * src/c++98/ios_init.cc (__ioinit): Define here instead if
>         the init_priority is usable.
>         * src/c++98/ios_base_init.h: New file.
> ---
>  libstdc++-v3/include/std/iostream      |  4 ++++
>  libstdc++-v3/src/c++98/ios_base_init.h | 12 ++++++++++++
>  libstdc++-v3/src/c++98/ios_init.cc     |  2 ++
>  3 files changed, 18 insertions(+)
>  create mode 100644 libstdc++-v3/src/c++98/ios_base_init.h
>
> diff --git a/libstdc++-v3/include/std/iostream b/libstdc++-v3/include/std/iostream
> index 70318a45891..ff78e1cfb87 100644
> --- a/libstdc++-v3/include/std/iostream
> +++ b/libstdc++-v3/include/std/iostream
> @@ -73,7 +73,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    ///@}
>
>    // For construction of filebuffers for cout, cin, cerr, clog et. al.
> +  // When the init_priority attribute is usable, we do this initialization
> +  // in the compiled library instead (src/c++98/ios_init.cc).
> +#if !__has_attribute(__init_priority__)
>    static ios_base::Init __ioinit;
> +#endif
>
>  _GLIBCXX_END_NAMESPACE_VERSION
>  } // namespace
> diff --git a/libstdc++-v3/src/c++98/ios_base_init.h b/libstdc++-v3/src/c++98/ios_base_init.h
> new file mode 100644
> index 00000000000..fe45e0e98a9
> --- /dev/null
> +++ b/libstdc++-v3/src/c++98/ios_base_init.h
> @@ -0,0 +1,12 @@
> +// This is only in a header so we can use the system_header pragma,
> +// to suppress the warning caused by using a reserved init_priority.
> +#pragma GCC system_header
> +
> +// If the target supports init priorities, set up a static object in the
> +// compiled library to perform the <iostream> initialization once and
> +// sufficiently early (so that the initialization runs first before any
> +// other global constructor when staticaly linking with libstdc++.a),
> +// instead of having to do so in each TU that includes <iostream>.
> +#if __has_attribute(init_priority)
> +static ios_base::Init __ioinit __attribute__((init_priority(90)));
> +#endif
> diff --git a/libstdc++-v3/src/c++98/ios_init.cc b/libstdc++-v3/src/c++98/ios_init.cc
> index 1b5132f1c2d..4016fcab785 100644
> --- a/libstdc++-v3/src/c++98/ios_init.cc
> +++ b/libstdc++-v3/src/c++98/ios_init.cc
> @@ -75,6 +75,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    extern wostream wclog;
>  #endif
>
> +#include "ios_base_init.h"
> +
>    ios_base::Init::Init()
>    {
>      if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, 1) == 0)
> --
> 2.38.1.385.g3b08839926
>
Florian Weimer Nov. 4, 2022, 4:57 p.m. UTC | #4
* Patrick Palka via Gcc-patches:

> This patch moves the global object for constructing the standard streams
> out from <iostream> and into the compiled library on targets that support
> the init_priority attribute.  This means that <iostream> no longer
> introduces a separate global constructor in each TU that includes it.
>
> We can do this only if the init_priority attribute is supported because
> we need to force that the stream initialization runs first before any
> user-defined global initializer in programs that that use a static
> libstdc++.a.

I think this breaks initialization of iostreams of shared objects that
are preloaded with LD_PRELOAD.  With the constructor, they initialize
iostreams once they are loaded via their own ELF constructors (even
before libstdc++'s ELF constructors run).  Without the constructor in
<iostream>, that no longer happens.

Thanks,
Florian
Patrick Palka Nov. 4, 2022, 5:47 p.m. UTC | #5
On Fri, 4 Nov 2022, Florian Weimer wrote:

> * Patrick Palka via Gcc-patches:
> 
> > This patch moves the global object for constructing the standard streams
> > out from <iostream> and into the compiled library on targets that support
> > the init_priority attribute.  This means that <iostream> no longer
> > introduces a separate global constructor in each TU that includes it.
> >
> > We can do this only if the init_priority attribute is supported because
> > we need to force that the stream initialization runs first before any
> > user-defined global initializer in programs that that use a static
> > libstdc++.a.
> 
> I think this breaks initialization of iostreams of shared objects that
> are preloaded with LD_PRELOAD.  With the constructor, they initialize
> iostreams once they are loaded via their own ELF constructors (even
> before libstdc++'s ELF constructors run).  Without the constructor in
> <iostream>, that no longer happens.

IIUC wouldn't that shared object depend on libstdc++.so and hence
libstdc++'s constructors would still run before the shared object's?

> 
> Thanks,
> Florian
> 
>
Florian Weimer Nov. 4, 2022, 6:16 p.m. UTC | #6
* Patrick Palka:

> On Fri, 4 Nov 2022, Florian Weimer wrote:
>
>> * Patrick Palka via Gcc-patches:
>> 
>> > This patch moves the global object for constructing the standard streams
>> > out from <iostream> and into the compiled library on targets that support
>> > the init_priority attribute.  This means that <iostream> no longer
>> > introduces a separate global constructor in each TU that includes it.
>> >
>> > We can do this only if the init_priority attribute is supported because
>> > we need to force that the stream initialization runs first before any
>> > user-defined global initializer in programs that that use a static
>> > libstdc++.a.
>> 
>> I think this breaks initialization of iostreams of shared objects that
>> are preloaded with LD_PRELOAD.  With the constructor, they initialize
>> iostreams once they are loaded via their own ELF constructors (even
>> before libstdc++'s ELF constructors run).  Without the constructor in
>> <iostream>, that no longer happens.
>
> IIUC wouldn't that shared object depend on libstdc++.so and hence
> libstdc++'s constructors would still run before the shared object's?

Hmm, right, we only reorder the symbol binding order, not the
initialization order.  The preloaded object will not participate in a
cycle with libstdc++, so I think this should indeed be a safe change.

Thanks,
Florian
Hans-Peter Nilsson Nov. 15, 2022, 4:10 p.m. UTC | #7
> From: Patrick Palka via Gcc-patches <gcc-patches@gcc.gnu.org>
> Date: Fri, 4 Nov 2022 16:05:25 +0100

> This patch moves the global object for constructing the standard streams
> out from <iostream> and into the compiled library on targets that support
> the init_priority attribute.  This means that <iostream> no longer
> introduces a separate global constructor in each TU that includes it.
> 
> We can do this only if the init_priority attribute is supported because
> we need to force that the stream initialization runs first before any
> user-defined global initializer in programs that that use a static
> libstdc++.a.
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look right?
> Unfortunately I don't have access to a system that truly doesn't support
> init priorities, so I instead tested that situation by artificially
> disabling the init_priority attribute on x86_64.
> 
> 	PR libstdc++/44952
> 	PR libstdc++/98108
> 
> libstdc++-v3/ChangeLog:
> 
> 	* include/bits/c++config (_GLIBCXX_HAS_ATTRIBUTE): Define.
> 	(_GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY): Define.
> 	* include/std/iostream (__ioinit): Define only if init_priority
> 	attribute isn't usable.
> 	* src/c++98/ios_init.cc (__ioinit): Define here instead if
> 	the init_priority is usable.
> 	* src/c++98/ios_base_init.h: New file.

This (r13-3707-g4e4e3ffd10f53e) broke statically linked programs
using iostreams (affected embedded targets + "native" with
-static).  For me it manifests as adding some 100+ fails to my
cris-elf autotester also repeatable using a native Debian 11
x86_64 build running the test-suite with -static like "make
check-gcc-c++ 'RUNTESTFLAGS=--target_board=unix/-static
old-deja.exp=15071.C'".

I opened PR107701 for it.

brgds, H-P
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config
index 50406066afe..f93076191d9 100644
--- a/libstdc++-v3/include/bits/c++config
+++ b/libstdc++-v3/include/bits/c++config
@@ -837,6 +837,18 @@  namespace __gnu_cxx
 
 #undef _GLIBCXX_HAS_BUILTIN
 
+#ifdef __has_attribute
+# define _GLIBCXX_HAS_ATTRIBUTE(B) __has_attribute(B)
+#else
+# define _GLIBCXX_HAS_ATTRIBUTE(B) 0
+#endif
+
+#if _GLIBCXX_HAS_ATTRIBUTE(init_priority)
+# define _GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY 1
+#endif
+
+#undef _GLIBCXX_HAS_ATTRIBUTE
+
 // Mark code that should be ignored by the compiler, but seen by Doxygen.
 #define _GLIBCXX_DOXYGEN_ONLY(X)
 
diff --git a/libstdc++-v3/include/std/iostream b/libstdc++-v3/include/std/iostream
index 70318a45891..5eaa9755d9a 100644
--- a/libstdc++-v3/include/std/iostream
+++ b/libstdc++-v3/include/std/iostream
@@ -73,7 +73,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   ///@}
 
   // For construction of filebuffers for cout, cin, cerr, clog et. al.
+  // When the init_priority attribute is usable, we do this initialization
+  // in the compiled library instead (see src/c++98/ios_init.cc).
+#if !_GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY
   static ios_base::Init __ioinit;
+#endif
 
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
diff --git a/libstdc++-v3/src/c++98/ios_base_init.h b/libstdc++-v3/src/c++98/ios_base_init.h
new file mode 100644
index 00000000000..f3087d1da3c
--- /dev/null
+++ b/libstdc++-v3/src/c++98/ios_base_init.h
@@ -0,0 +1,9 @@ 
+// This is only in a header so we can use the system_header pragma,
+// to suppress the warning caused by using a reserved init_priority.
+#pragma GCC system_header
+
+#if !_GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY
+# error "This file should not be included for this build"
+#endif
+
+static ios_base::Init __ioinit __attribute__((init_priority(90)));
diff --git a/libstdc++-v3/src/c++98/ios_init.cc b/libstdc++-v3/src/c++98/ios_init.cc
index 1b5132f1c2d..954fa9f29cf 100644
--- a/libstdc++-v3/src/c++98/ios_init.cc
+++ b/libstdc++-v3/src/c++98/ios_init.cc
@@ -75,6 +75,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   extern wostream wclog;
 #endif
 
+#if _GLIBCXX_HAVE_ATTRIBUTE_INIT_PRIORITY
+# include "ios_base_init.h"
+#endif
+
   ios_base::Init::Init()
   {
     if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, 1) == 0)