I am working on embedded systems as beginner and have come across files like start.s or cstart files that run before main() function begins. What is the purpose of these or similar files? What information we are telling the system? I've heard of initialization but don't know exactly what that is.

  • 3
    \$\begingroup\$ If you have found files like start.s or cstart then you can probably open these files and try to understand them, do not be so lazy. If you are so lazy then you can use google before askig questions: stackoverflow.com/questions/3393611/… \$\endgroup\$ – Al Bundy Sep 20 '16 at 18:18
  • \$\begingroup\$ I tried to read them but couldn't decrypt what they are doing. \$\endgroup\$ – doubleE Sep 20 '16 at 18:21
  • 2
    \$\begingroup\$ Your question should be more specific. There is much information people has to guess the way it is asked. For example: what kind of processor? which compiler? Also, you should let us know what have you done to solve your question before asking. Why is it cryptic? is it written in assembler? Finally, have you ever read the processor datasheet? have you gone through the compiler manual? \$\endgroup\$ – Krauss Sep 20 '16 at 18:30
  • \$\begingroup\$ Related: What should happen before main(). \$\endgroup\$ – Lundin Oct 31 '18 at 15:00

It is completely dependent on the compiler and architecture, but generally that code initializes the most basic hardware required for the rest of the code to run. The code for example:

  • Defines the reset vectors

  • Defines the layout of data in memory (many systems use a linker script instead)

  • Defines the addresses of interrupt service routines in a big table (the interrupt vector table)

  • Initializes CPU registers, e.g. the stack pointer

  • Configures the core clock

In addition, that section also serves the runtime needs of the programming language used. It:

  • Initializes whatever function parameter passing system used

  • Initializes global variables by e.g. copying flash contents to RAM and zero-initializing memory

  • If dynamic memory allocation is used, initializes the heap

  • If floating point math is enabled, initializes the FPU (if available) or initializes the floating point library

  • If exceptions are used, initializes exception handling.


Somewhat related question: Who receives the value returned by main()?

main() is an ordinary C function, so it requires certain things to be initialized before it is called. These are related to:

  • Setting up a valid stack
  • Creating a valid argument list (usually on the stack)
  • Initializing the interrupt-handling hardware
  • Initializing global and static variables (including library code)

The last item includes such things as setting up a memory pool that malloc() and free() can use, if your environment supports dynamic memory allocation. Similarly, any form of "standard I/O" that your system might have access to will also be initialized.

Pretty much anything else is going to be application-dependent, and will have to be initialized from within main(), before you enter your "main loop".

  • 1
    \$\begingroup\$ Except for maybe setting up an MMU (memory management unit), I have never seen the C startup code (crt0.s for example) actually do any I/O initialization, this is usually done right at the top of main using a series of C function calls. \$\endgroup\$ – tcrosley Sep 20 '16 at 18:35
  • \$\begingroup\$ @tcrosley: Like I said, "might have access to". It's rare, but I've seen it done. In most systems with MMUs, you're running under an OS, not on the bare metal, so the environment for main() is more like running on a desktop system. \$\endgroup\$ – Dave Tweed Sep 20 '16 at 18:38
  • 1
    \$\begingroup\$ main is most certainly not an ordinary C function. There are many very specific rules that apply to main, such as no prototype, implicit return 0 etc. The allowed forms of main are also special and dictated by the given compiler - so unlike when declaring/defining regular functions, the programmer cannot decide the function format. \$\endgroup\$ – Lundin Sep 21 '16 at 11:06
  • \$\begingroup\$ Regarding MMU, it is quite common that even simple microcontrollers have some form limited MMU. Some devices allow custom memory mapping of registers, RAM, flash etc. As for more complex devices, they don't necessarily have an OS. I've done some bare metal Power PC projects - it has a rather complex MMU which obviously needs to be initialized very early on, even before the stack is initialized. \$\endgroup\$ – Lundin Sep 21 '16 at 11:09
  • \$\begingroup\$ As for I/O initialization from start-up code, I've never seen that either. I have however written such code myself, for example when the MCU has all pins set as input by default. On some sensitive applications, you might then want to set them as outputs as soon as possible for EMC reasons. Also, professional programs initialize fundamental safety features like watchdog and brown-out detect as early as possible, often before main. Amateur/hobbyist/wannabe programmers often do the massive copy-down of data to all RAM cells in .data and .bss with no wdog or LVD yet activated. \$\endgroup\$ – Lundin Sep 21 '16 at 11:19

On a typical embedded system, startup code will at minimum will have to load all initialized variables with their defined values and zero out all uninitialized variables. Depending upon the hardware platform, it may also have to configure the CPU stack pointer [on some hardware platforms, a reset will automatically set the stack pointer to the top of memory, but on other platforms it must be set manually] or configure various other features in the CPU or memory controller.

The startup code is usually pretty short and simple, and some platforms may document how it works and allow a user to substitute something else (e.g. if an embedded system will need to have a user-supplied startup routine copy some code from a serial flash chip into RAM and then execute it, it may make sense to have initialized variables be part of the code image, rather than having their initial values be part of the code image which is copied to another area of RAM on startup but then ignored thereafter).


In a traditional GNU/Linux desktop toolchain, most of such code is implemented by glibc and is contained in objects such as crti.o, you can get the full list with gcc --verbose main.c.

Basically, glibc, is setting things up so that further glibc calls will work properly.

Therefore, you can learn what is happening by reading the source of glibc, e.g. sysdeps/x86_64/crti.S.

TODO: how to step debug that code with the source? Modifying those objects itself is a major pain unless you know dark arts.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.