Shut Down C++ Embedded Systems with Calling the global Destructors

If using C++ on an embedded target, you depend on the constructors for global objects being called by the startup code. While in many cases an embedded system won’t stop, so you don’t need to call the global C++ destructors, this is still something to consider for a proper shutdown.

Calling OOP Destructors after leaving main()

Constructors at startup

When using the NXP MCUXpresso SDK with C++, the constructors are getting called as you expect it.

Constructors called during startup code

This is accomplished by a call to __libc_init_array():

#if defined (__cplusplus)
    //
    // Call C++ library initialisation
    //
    __libc_init_array();
#endif

The data necessary to call the destructors is collected by the linker in a section marked with __init_array_start and __init_array_end as with the entry below in the linker file:

            . = ALIGN(4);
            __init_array_start = .;
            KEEP (*(SORT(.init_array.*)))
            KEEP (*(.init_array))
            __init_array_end = .;

Unfortunately, things are not setup for the destructors, so we have to configure this manually with the steps below.

Destructors

First, we have to tell in the linker file that we want to collect the information about the destructors. To do this, we have to change the linker script file and to prevent the linker to change it. Go to the project settings and turn off the ‘Manage linker script‘:

Disabled Manged Linker Script

With this, the linker won’t change the linker file any more.

Next, add a section with __fini_array_start and __fini_array_end to the linker file:

            /* array of function pointers for the the static destructors */
            __fini_array_start = .;
            KEEP(*(SORT(.fini_array.*)))
            KEEP(*(.fini_array))
            __fini_array_end = .;
            
            KEEP(*(.fini));

Best to place this right after the __init_array_start:

Collecting Destructor Information

Next, I have to add -fno-use-cxa-atexit to the compiler options:

-fno-use-cxa-atext C++ compiler option

Now I can call the destructors as part of the shutdown process, using following interface:

extern "C" {
    extern void __libc_fini_array(void);
}

This could be called in the startup code in case it returns from main(), or as part of the exit() code of the application.

calling destructors

That way, all the global objects get released at the end of the application:

Destructors called

Summary

While many embedded applications never shut down, in a clean system design there are ways to power down a system in a proper way. In the case of C++ this includes cleaning things up and calling the destructors oft he global objects too.

Happy destructing šŸ™‚

Links

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.