In the OOP world, global objects get initialized with a constructor and destroyed at the end with a destructor. Interestingly, the GNU gcc has attributes to mark functions as constructor and destructors, which can greatly simply system startup and shutdown, even if not using C++.

With the GNU gcc compiler, I can mark functions with an attribute, so they get called before entering main() or after exit of main(). The attribute works both in C and C++, but it especially useful in C to initialize modules in an automated way.
Global Constructors and Destructors
In C++, when having global objects or variables, then they need to be constructed automatically at program startup, and destructed at program shutdown.
Consider the following example:
class Car {
private: int price;
public:
Car(void) { // constructor
price = 1000; // initial base price for every car
}
~Car(void) { // destructor
price = -1;
}
void SetPrice(int price) { // setter method
this->price = price;
}
};
// global variables
static Car c; // global object, shall be initialized by startup (constructor)
static int i; // initialized by startup (zero-out)
static int j = 0x1234; // initialized by startup (copy-down)
Here the program startup will call the constructor of Car c and initialize it that way, in a similar way as the startup zero-out and startup copy-down initializes variables in C. The difference is that zero-out and copy-down simply initializes the value/memory, where the constructor and destructor are function calls. All this will be handled by the startup code, and the initialization happens before calling main().
The idea is to use that concept of calling function calls before main() for normal C modules, to have them initialized and de-initialized, so I don’t have to do this manually in my code.
GCC Constructor and Destructor Attributes
The solution is to take advantage of special GNU gcc compiler attributes.
From the gcc user manual:
https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Function-Attributes.html
constructordestructor
Theconstructorattribute causes the function to be called automatically before execution entersmain (). Similarly, thedestructorattribute causes the function to be called automatically aftermain ()has completed orexit ()has been called. Functions with these attributes are useful for initializing data that will be used implicitly during the execution of the program.
With this, I can mark my initialization and de-initialization routines with the attributes, for example:
__attribute((destructor))
void STEPPER_Deinit(void) {
...
}
__attribute((constructor))
void STEPPER_Init(void) {
...
}
I usually have Deinit() functions in my driver, in case I need to shut-down a peripheral and initialize it. As embedded systems usually ‘never end’, the Deinit() and as such the destructor might not be needed and used.
It is possible to mark a function both as constructor and destructor. It can be mixed with the prototype/declaration and definition, and vice versa:
__attribute((constructor)) void CalledAsConstructerAndDestructor(void)
__attribute((destructor)) void CalledAsConstructerAndDestructor(void) {
/* called both in constructing and destructing phase */
}
Order of Calls
In many cases, you might want to have control over the order of calls. It is possible to give the destructor and constructor a ‘priority’, with lower numbers the higher urgency, for example:
__attribute__ ((constructor(101)))
void myInit(void) {
...
}
Values from 0 to 100 are reserved and used for example by the gcov. So any number from 101- 65535 can be used. If no value/argument is provided, then they get a lower priority than the ones with the number.
But VS Code Intellisense will flag the arguments as an error which can be ignored (see bug report).
attribute "constructor" does not take arguments C/C++(1094)

To suppress the error, the following can be used:
#if __INTELLISENSE__
#pragma diag_suppress 1094
#endif
Linker File
To have the init and de-init called by the startup code, I have to make sure the objects are linked and listed in a special section. Below is the added section placement for constructors and destructors:
.text : ALIGN(4)
{
FILL(0xff)
__vectors_start__ = ABSOLUTE(.) ;
KEEP(*(.isr_vector))
/* Global Section Table */
. = ALIGN(4) ;
__section_table_start = .;
__data_section_table = .;
LONG(LOADADDR(.data));
LONG( ADDR(.data));
LONG( SIZEOF(.data));
LONG(LOADADDR(.data_RAM2));
LONG( ADDR(.data_RAM2));
LONG( SIZEOF(.data_RAM2));
__data_section_table_end = .;
__bss_section_table = .;
LONG( ADDR(.bss));
LONG( SIZEOF(.bss));
LONG( ADDR(.bss_RAM2));
LONG( SIZEOF(.bss_RAM2));
__bss_section_table_end = .;
__section_table_end = . ;
/* End of Global Section Table */
*(.after_vectors*)
/* Kinetis Flash Configuration data */
. = 0x400 ;
PROVIDE(__FLASH_CONFIG_START__ = .) ;
KEEP(*(.FlashConfig))
PROVIDE(__FLASH_CONFIG_END__ = .) ;
ASSERT(!(__FLASH_CONFIG_START__ == __FLASH_CONFIG_END__), "Linker Flash Config Support Enabled, but no .FlashConfig section provided within application");
/* End of Kinetis Flash Configuration data */
*(.text*)
*(.rodata .rodata.* .constdata .constdata.*)
. = ALIGN(4);
/*-------------------------------------------------------------------------- */
/* Constructors and Destructors */
. = ALIGN(4);
KEEP(*(.init))
. = ALIGN(4);
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;
. = ALIGN(4);
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
KEEP(*(.fini));
. = ALIGN(4);
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*crtend.o(.ctors))
. = ALIGN(4);
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*crtend.o(.dtors))
. = ALIGN(4);
/* End Constructors and Destructors */ /*-------------------------------------------------------------------------- */
/*-------------------------------------------------------------------------- */
} > PROGRAM_FLASH
The same linker placement is used for C++ constructors and destructors too.
Startup Code
In the Startup code I need to make sure that the constructors and destructors get called. In a C++ system, this should be already implemented. In a C application, I have to make sure two functions in the C library get called.
First I have to add two prototypes:
extern void __libc_init_array(void); /* call constructors */
extern void __libc_fini_array(void); /* call destructors */
Then I need to make sure they get called before and after main():

Library Runtime
If you get an error during linking with
undefined reference to `_init'
then you need to check your linker file, because you have to add the crt*.o runtime support files which perform the initialization and de-initialization. Below is what I have in my linker files:
GROUP ( "libc.a" "libgcc.a" "libm.a" "libc_nano.a" /* startup with constructor/destructor support: */ "crti.o" "crtn.o" "crtbegin.o" "crtend.o" )
Summary
With this, the constructors get called before main, and the destructors get called if I would leave main:

In summary, I have now an elegant way to call initialization and de-initialization in my application.
Happy constructing and destructing 🙂
Interesting! I’ll have to have a play with this.
Heads up: you’ve got the prototype comments backwards under the “Startup Code” section.
LikeLike
Hi Stephen,
thanks for the heads up: I have it fixed.
LikeLike
While I understand the allure of using these, as I have been using them for years. They can made C code be modularized, in theory, so that there are no (de)init code needed in main(). Alas I have found that they make debugging hard because of their fixed priority nature, and in future code I’m going to avoid using them. Leave that up to a proper task manager or RTOS. The could still be useful to get control of I/O at the earliest opportunity, to get it in a safe passive state until the system is ready to run.
hardware_init_hook() and software_init_hook() that are called by newlib _start() after bss is zeroed may be a better place for such startup code.
Also be careful that you do not have double calls to __libc_X_array().
Some start up code such as newlib _start() already calls __libc_init_array().
Which other than wasting time may not be noticed.
Double calls to __libc_fini_array() are very likely to cause a bus fault.
Consider a UART that turns itself off, then shutdown’s its clock. On the second call the UART register access will result in a bus fault because there is no clock. This is where debugging can get hard.
LikeLiked by 1 person
Hi Bob,
thanks for your thoughts and insights! Yes, using this approach has definitely some pros and cons. I think the feature should not be over-used, and used for well defined things, as you mention.
Double initialization is bad in most cases, but I usually avoid to add an ‘isInitialized’ flag to modules if memory usage is a concern.
As for the startup calling fini and ini: most startups especially around C SDKs even don’t bother and don’t call it, I have even seen C++ startup code which misses to initialize the global constructors. For this it is always good to know how they work, so in case this can be fixed.
Thanks again!
LikeLike
we should be trying to move away from c++ and compiler specific attributes.
LikeLiked by 3 people
Moving gradually to C++ would be a better approach imho.
LikeLike