In embedded applications, since we are working very close to the hardware and since we are typically working with limited space, strict power requirements, and exact timing restrictions, it is very important to know exactly what happens when we run a program from the beginning to the end. Most developers (especially those who come from the non-embedded development world) usually assume that running a C program starts from the main() function. This is not true. There are a lot of steps that take place before main() is executed and understanding those steps will help a lot in gaining a better understanding of how the system works and ultimately will be extremely helpful in some debug situations. For example, in embedded systems it is possible to have your program crash before even reaching main(). Understanding what happens before main() will make you better at debugging these type of crashes. The best way to learn is to run a very simple program (eg. a program with a main() function that simply returns zero) ideally on a microcontroller and use gdb to step through the code. While what happens before main(), is architecture and platform dependent, there are some generic steps that usually take place. Here they are in no particular order:

  • _The hardware is initialized._ The most important part of this step is setting up the clock that the CPU needs to run the code. This is usually done by programming a PLL (essentially a programmable clock). If there is not clock going to the CPU, the CPU is essentially dead. The other part of hardware initialization is initializing the interrupt handling hardware.
  • _Memory segments are initialized._ Memory segments such as .bss (for uninitialized data), .data (for initialized data such as static variables, global variables, local static variables, addresses of functions, and function pointers), and .text (where the actual code resides) are initialized and a valid stack is set up.
  • _Command line arguments are received._ This may not be relevant in embedded systems as in embedded systems we don’t usually call main() with arguments
  • _The stack pointer is configured._ This is necessary because the program needs to know where to start from.

Note that some microcontrollers may require a start.c or cstart file that has the initialization code (whether this file is manually created or auto generated). Now that we know what happens before main(), we might wonder if there’s a way we can control what happens before main, or if there is a way we can write our own version of what happens before main(). In C, the answer is largely no. Whatever happens before main() is largely dependent on your architecture and compiler. However this is in fact possible in C++. One way you can do this in C++ is by declaring a global class object. Since global variables are initialized before main(), the constructor of the class you have initialized as global will run before main(). You can therefore place the code you want to run before main() in such a constructor.