https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117630
Bug ID: 117630 Summary: Useless atexit entry for generic_category_instance and system_category_instance Product: gcc Version: 14.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: rdiez-2006 at rd10 dot de Target Milestone: --- I have this embedded firmware project of mine, which uses Newlib: https://github.com/rdiez/DebugDue It is the template for other similar bare-metal projects I have. Even though some targets have very little SRAM (like 16 KiB), I am still using C++ and exceptions. The project above documents how I configure GCC to that effect. Up until GCC 13, I have been doing this check: if ( _GLOBAL_ATEXIT != nullptr ) { Panic( "Unexpected entries in atexit table." ); } On devices with very little RAM and without an operating system, initialisation and destruction can be tricky. With the check above, I am making sure that nothing unexpected comes up in that respect. I am initialising all static objects manually anyway, to prevent any ordering surprises (the initialisation order of C++ static objects can be problematic too). The check above fails with GCC 14.2. Apparently, 2 destructors for generic_category_instance and system_category_instance are generated as atexit entries. Because of the way that Newlib works, that wastes 400 bytes of SRAM, which corresponds to sizeof( _global_atexit0 ). The structure has room for 32 atexit calls (because of some ANSI conformance), but my project is only using 2 entries for those destructors. It is not just a RAM wastage issue, the main issue is that it interferes with my automatic method of detecting unexpected destruction surprises. This is very similar to this issue I reported 2 years ago, which got fixed for GCC 12.4: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107500 In this occasion, the interesting code lives here: gcc-14.2.0/libstdc++-v3/include/std/system_error gcc-14.2.0/libstdc++-v3/src/c++11/system_error.cc The code tries to make those 2 instances immortal with a union trick, see "struct constant_init". GCC still fails to optimise away such empty destructors (this has been reported some years ago) and generates useless atexit() entries for them. The interesting thing is that base class std::error_category and derived classes generic_error_category and system_error_category seem to be empty of any data members. There isn't anything to destroy anyway. I am guessing that the C++ standard requires these objects to be static, and libstdc++ has to revert to ugly tricks because of C++'s well-known "static initialization order fiasco". This std::error_category area is complex, I am out of my depth here. As far as I can tell, there are other derived classes like future_error_category and iostream_category, but only generic_category_instance and system_category_instance cause problems in my project. Routine iostream_category() uses a similar union trick, but does not come up. I am guessing the reason is that my project does not use iostream at all. Is there a way I can tell who is using system_category() and generic_category()? This is surprising for me, as my bare metal project has no OS and barely uses any errno. There isn't even support for FILE and the like. The code base actually uses little C++ trickery. I didn't even know that error categories was a thing. Perhaps there is some standard C++ feature which I can disable, in order to stop usage of those 2 error categories. I could patch GCC when building the toolchain. Does anybody have any idea how to patch system_category etc. away, without breaking too much?