When linking statically to libstdc++.a (or to libstdc++_nonshared.a in the RHEL devtoolset compiler) there's a static initialization order problem where user code might be constructed before the std::chrono::tzdb_list globals, and so might try to use them after they've already been destroyed.
Use the init_priority attribute on those globals so that they are initialized early. Since r15-7511-g4e7f74225116e7 we can disable the warnings for using a reserved priority using a diagnostic pragma. libstdc++-v3/ChangeLog: PR libstdc++/118811 * src/c++20/tzdb.cc (tzdb_list::_Node): Use init_priority attribute on static data members. * testsuite/std/time/tzdb_list/pr118811.cc: New test. --- Tested x86_64-linux. Pushed to trunk. This needs to be backported too, but will have to use the system header hack to use the attribute without warnings. libstdc++-v3/src/c++20/tzdb.cc | 11 +++++--- .../testsuite/std/time/tzdb_list/pr118811.cc | 25 +++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 libstdc++-v3/testsuite/std/time/tzdb_list/pr118811.cc diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc index 7e8cce7ce8c..c3bb6a12ccc 100644 --- a/libstdc++-v3/src/c++20/tzdb.cc +++ b/libstdc++-v3/src/c++20/tzdb.cc @@ -133,6 +133,8 @@ namespace std::chrono // of this type gives them access to the private members of time_zone // and tzdb, without needing them declared in the <chrono> header. + // The tzdb_list singleton. This doesn't contain the actual linked list, + // but it has member functions that give access to it. static tzdb_list _S_the_list; #if USE_ATOMIC_SHARED_PTR @@ -177,15 +179,16 @@ namespace std::chrono // Implementation of the private constructor used for the singleton object. constexpr tzdb_list::tzdb_list(nullptr_t) { } - // The tzdb_list singleton. This doesn't contain the actual linked list, - // but it has member functions that give access to it. +#pragma GCC diagnostic ignored "-Wprio-ctor-dtor" + + [[gnu::init_priority(99)]] constinit tzdb_list tzdb_list::_Node::_S_the_list(nullptr); - // Shared pointer to the first Node in the list. + [[gnu::init_priority(99)]] constinit tzdb_list::_Node::head_ptr tzdb_list::_Node::_S_head_owner{nullptr}; #if USE_ATOMIC_LIST_HEAD - // Lock-free access to the first Node in the list. + [[gnu::init_priority(99)]] constinit atomic<tzdb_list::_Node*> tzdb_list::_Node::_S_head_cache{nullptr}; #endif diff --git a/libstdc++-v3/testsuite/std/time/tzdb_list/pr118811.cc b/libstdc++-v3/testsuite/std/time/tzdb_list/pr118811.cc new file mode 100644 index 00000000000..3968be3f0ec --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/tzdb_list/pr118811.cc @@ -0,0 +1,25 @@ +// { dg-do run { target c++20 } } +// { dg-require-effective-target tzdb } +// { dg-require-effective-target cxx11_abi } +// { dg-require-static-libstdcxx } +// { dg-additional-options "-static-libstdc++" } + +#include <chrono> + +struct Global +{ + Global() + { + (void) std::chrono::current_zone(); // initialize tzdb on first use + } + + ~Global() + { + (void) std::chrono::current_zone(); // attempt to use it again on exit + } + +} global; + +int main() +{ +} -- 2.48.1