Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training.

    Featured resources

    • OpenShift AI learning
    • Open source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.
    • Guided learning
      Receive custom learning paths powered by our AI assistant.
    • See all learning

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

A leaner <iostream> in libstdc++ for GCC 13

April 3, 2023
Patrick Palka
Related topics:
CompilersC, C#, C++Linux
Related products:
Red Hat Enterprise Linux

    One of the many enhancements coming to libstdc++ shipped in GCC 13, which is expected to be released in May 2023, addresses an old pain point of the <iostream> header. In current versions of libstdc++, including <iostream> in a translation unit (TU) introduces a global constructor into the compiled object file, one that is responsible for initializing the standard stream objects std::cout, std::cin, etc., on program startup.

    In libstdc++ for GCC 13, this will be no more, as we’ve moved the initialization of the standard stream objects into the shared library. The benefit of this is reduced executable size, improved link times, and improved startup times for C++ programs that make heavy use of <iostream>.

    An example

    Consider the canonical C++ Hello World program:

    #include <iostream>
    
    
    
    int main() {
    
        std::cout << "Hello world!\n";
    
    }

    At some point in your journey as a C++ programmer, you've probably compiled this program and inspected the resulting assembly.  Using GCC 12.2 as the compiler yields the following assembly:

         .LC0:
    
         .string "Hello world!\n"
    
        main:
    
          subq $8, %rsp
    
          movl $.LC0, %esi
    
          movl $_ZSt4cout, %edi # std::cout
    
          call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
    
          movl $0, %eax
    
          addq $8, %rsp
    
          ret
    
        _GLOBAL__sub_I_main:
    
          subq $8, %rsp
    
          movl $_ZStL8__ioinit, %edi # std::__ioinit
    
          call std::ios_base::Init::Init() [complete object constructor]
    
          movl $__dso_handle, %edx
    
          movl $_ZStL8__ioinit, %esi # std::__ioinit
    
          movl $_ZNSt8ios_base4InitD1Ev, %edi # std::ios_base::Init::~Init()
    
          call __cxa_atexit
    
          addq $8, %rsp
    
          ret

    This is a surprising amount of assembly code for Hello World.  Notably, alongside the expected main definition which invokes the appropriate operator<< overload, somehow a global initializer (the _GLOBAL__sub_I_main symbol) snuck in, one which seems to construct an object __ioinit of type std::ios_base::Init (and schedules destruction of the object at program exit).  One might wonder, how did that get there and what is its purpose?

    In contrast, if we compile with GCC trunk, we instead get the following:

    .LC0:
    
         .string "Hello world!\n"
    
        main:
    
          subq $8, %rsp
    
          movl $.LC0, %esi
    
          movl $_ZSt4cout, %edi
    
          call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
    
          movl $0, %eax
    
          addq $8, %rsp
    
          ret

    This notably emits no such global initializer (and is much more in line with what one would expect the resulting assembly to be).  Instead, an equivalent initializer is present in libstdc++.so and will run upon dynamic loading of the library (we’ll touch upon the details in a later section).

    Why the __ioinit object

    The global initializer we see in the GCC 12.2 output corresponds to the static global object __ioinit that's defined in <iostream>:

      // For construction of filebuffers for cout, cin, cerr, clog et. al.
    
      static ios_base::Init __ioinit;

    As the comment above it suggests, the purpose of this object is ultimately to ensure that the standard stream objects std::cout, std::cin, etc., are properly initialized (via the ios_base::Init::Init constructor) before they are used in the program by either main() proper or earlier during the initialization of another global object.

    Thus, including <iostream> implicitly defines a static global object of type ios_base::Init within the TU.  This approach gets the job done because C++ guarantees global objects within one TU are initialized in the order in which they're defined, so we can ensure that the stream objects are usable even during the startup (and shutdown) phase of a program, e.g.:

        #include <iostream>
    
    
    
        struct A {
    
            A() { std::cout << "A::A()\n"; }
    
            ~A() { std::cout << "A::~A()\n"; }
    
        };
    
    
    
        static A a; // Works because __ioinit is defined within the TU first

    A tempting and more obvious alternative might be to perform initialization of the standard stream objects in the stream objects' constructors themselves (which effectively must be defined in the compiled-in part of the library – libstdc++.so or libstdc++.a -- rather than in a header because std::cout, et al. are not inline objects).  But this won't work because C++ gives no guarantees about global object initialization order across TUs, and neither do linkers by default give similar guarantees across object files.

    While the static global approach works, there's a significant drawback: a distinct global object will be defined for every TU that includes <iostream> and will persist all the way into the final executable.  Thus at program startup, the constructor (and destructor) for ios_base::Init will redundantly run once for every constituent TU that includes <iostream>, leading to code bloat and slower link and startup times.

    A better approach

    In GCC 13, we essentially moved the __ioinit definition:

      static ios_base::Init __ioinit;

    from the <iostream> header and into the compiled library sources.  This was carefully done with the help of the non-standard init_priority attribute, which gives more control over inter-TU object initialization order than what standard C++ provides.  As a result, TUs that include <iostream> are no longer encumbered by this "invisible" global __ioinit object.

    As you might expect, this change is backward-compatible with programs compiled against earlier versions of libstdc++ because changing the <iostream> header won’t affect TUs that were compiled against the older <iostream> header, and the initialization that is now also performed within the compiled library is idempotent.

    Most modern platforms support the init_priority attribute; on platforms that lack such support, we fall back to the old way of defining __ioinit in <iostream>.

    Conclusion

    In GCC 13's libstdc++, we've revisited an age-old implementation decision of how the standard stream objects get defined.  The new approach is better in many ways, and it is more in line with the zero-cost abstraction philosophy of C++. This is one of many enhancements to look forward to in the upcoming release of GCC 13; a more complete list of changes can be found at gnu.org.

    Last updated: August 14, 2023

    Related Posts

    • New C features in GCC 13

    • New C++ features in GCC 12

    • The GDB developer's GNU Debugger tutorial, Part 1: Getting started with the debugger

    • GCC's new fortification level: The gains and costs

    • The state of static analysis in the GCC 12 compiler

    • Use compiler flags for stack protection in GCC and Clang

    Recent Posts

    • Trusted software factory: Building trust in the agentic AI era

    • Build a zero trust AI pipeline with OpenShift and RHEL CVMs

    • Red Hat Hardened Images: Top 5 benefits for software developers

    • How EvalHub manages two-layer Kubernetes control planes

    • Tekton joins the CNCF as an incubating project

    What’s up next?

    Advanced Linux Commands tile card - updated

    This cheat sheet presents a collection of Linux commands and executables for developers who are using the Linux operating system in advanced programming scenarios.

    Get the cheat sheet
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Chat Support

    Please log in with your Red Hat account to access chat support.