Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

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

Share:

    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

    • Meet the Red Hat Node.js team at PowerUP 2025

    • How to use pipelines for AI/ML automation at the edge

    • What's new in network observability 1.8

    • LLM Compressor: Optimize LLMs for low-latency deployments

    • How to set up NVIDIA NIM on Red Hat OpenShift AI

    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

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    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

    Red Hat legal and privacy links

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

    Report a website issue