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() {}