Constructors and Destructors¶
Metal implements a mechanism for registering constructors and destructors to run before main and upon exit. The purpose of the Metal init API is to provide a unified mechanism for registering constructors for both users and drivers, as well as to provide a mechanism for manually overriding or disabling the constructors/destructors entirely.
Defining a Constructor/Destructor¶
Constructors and destructors can be defined with the following macros:
-
METAL_CONSTRUCTOR
(function_name) Define a Metal constructor.
Functions defined with METAL_CONSTRUCTOR will be added to the list of Metal constructors. By default, these functions are called before main by the metal_init() function.
-
METAL_CONSTRUCTOR_PRIO
(function_name, priority) Define a Metal constructor with a given priority.
The priority argument should be an integer between 0 and 9999, where 0 is the highest priority (runs first) and 9999 is the lowest priority (runs last).
Functions defined with METAL_CONSTRUCTOR_PRIO will be added to the list of Metal constructors. By default, these functions are called before main by the metal_init() function.
-
METAL_DESTRUCTOR
(function_name) Define a Metal destructor.
Functions defined with METAL_DESTRUCTOR will be added to the list of Metal destructors. By default, these functions are called on exit by the metal_fini() function.
-
METAL_DESTRUCTOR_PRIO
(function_name, priority) Define a Metal destructor with a given priority.
The priority argument should be an integer between 0 and 9999, where 0 is the highest priority (runs first) and 9999 is the lowest priority (runs last).
Functions defined with METAL_DESTRUCTOR_PRIO will be added to the list of Metal destructors. By default, these functions are called on exit by the metal_fini() function.
For example:
METAL_CONSTRUCTOR(constructor_hello) {
puts("Hello from before main!\n");
}
METAL_DESTRUCTOR_PRIO(destructor_goodbye, METAL_INIT_HIGHEST_PRIORITY) {
puts("Program exiting, goodbye.\n");
}
The above sample defines the functions constructor_hello()
and
constructor_goodbye()
and registers them to be run by metal_init()
and
metal_fini()
.
-
void
metal_init
(void) Call all Metal constructors.
Devices supported by Metal may define Metal constructors to perform initialization before main. This function iterates over the constructors and calls them in turn.
You can add your own constructors to the functions called by metal_init() by defining functions with the METAL_CONSTRUCTOR() macro.
This function is called before main by default by metal_init_run().
-
void
metal_fini
(void) Call all Metal destructors.
Devices supported by Metal may define Metal destructors to perform initialization on exit. This function iterates over the destructors and calls them in turn.
You can add your own destructors to the functions called by metal_fini() by defining functions with the METAL_DESTRUCTOR() macro.
This function is called on exit by default by metal_fini_run().
Default Behavior¶
By default, Metal constructors and destructors are run before main and upon exit
respectively. This ensures that constructors defined by Metal and
Metal device drivers are called by default before main()
. For example, targets
with the “sifive,uart0” UART device set as stdout-path
automatically configure
the UART’s clock divider to the requested baud rate using a Metal constructor.
The default control flow looks like the following:
_start
…
metal_init_run
metal_init
constructor_1
constructor_2
…
…
main
…
exit
…
metal_fini_run
metal_fini
destructor_1
destructor_2
…
…
Note metal_init_run()
and metal_fini_run()
in the above flow graph.
-
void
metal_init_run
(void) Weak function to call metal_init() before main.
This function calls metal_init() before main by default. If you wish to replace or augment this call to the Metal constructors, you can redefine metal_init_run()
-
void
metal_fini_run
(void) Weak function to call metal_fini() before main.
This function calls metal_fini() at exit by default. If you wish to replace or augment this call to the Metal destructors, you can redefine metal_fini_run()
The purpose of these wrapper functions is to allow manual override by application code.
Preventing Constructors/Destructors from Running¶
You can prevent Metal constructors and destructors from running by redifining
metal_init_run()
and metal_fini_run()
in your application:
void metal_init_run() {}
void metal_fini_run() {}