https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117630
--- Comment #13 from R. Diez <rdiez-2006 at rd10 dot de> --- > Those two could be combined into one struct so there's only one destructor > registration instead of two. Thanks for your help, but reducing the number of atexit calls doesn't really help me much. As I said, the first atexit block reserves 32 slots at once, allegedly because of some ANSI conformance. Anyway, I am getting increasingly confused. File "libstdc++-v3/include/std/system_error" is virtually unchanged between gcc-13.3.0 and gcc-14.2.0, and file "system_error.cc" hasn't changed very much either, so the union trick is the same. That means that 'generic_category_instance' and 'system_category_instance' were there before. Why am I getting these new atexit() calls now then? I did some further testing. I took a rather small firmware of mine which does not use std::string at all, and no _GLOBAL__sub_I__ZSt20__throw_system_errori was generated. I add the same union trick code, and it did. I kept reducing that code, and this is what I found. When I add the following code, _GLOBAL__sub_I__ZSt20__throw_system_errori with the 2 atexit calls for generic_category_instance and system_category_instance: class test_class_rdiez { virtual void test ( void ) const throw() { std::string str; } }; static test_class_rdiez instance_rdiez; It is the mere presence of a virtual method which uses an std::string what triggers those atexit calls. If I remove 'virtual' or 'std::string str;' from the code above, the atexit calls disappear. I am now thinking that some C++ exception support code somewhere is calling __throw_system_error(), but I do not know enough to pin-point the location. That C++ support code has probably changed between GCC 13 and 14, and that is why I am now running into my "atexit should be empty" assertion.