Even though C is a relatively low-level language, some assembly code is needed to get the process in an acceptable state to the language standard.
First of all,
main isn't the standard entry point,
_start is. This function
is provided by your compiler, typically from a file called
environand the auxiliary vector from the stack (see the links to LWN in Process creation for stack content details)
__libc_start_user, which first calls the constructors of all global (C++) variables, then calls
exitautomatically as well.
Of course, it's huge, but we can provide our own: step 3 can be skipped, 1 and 2
are really small to implement in assembly, and 4 is replaced by calling
Of course, the second step can be skipped as well when you don't need
However, when external libs need to read from the environment (eg. X11 needs
$DISPLAY), you still have to call
__libc_start_user; it's a portable
interface that asks for
main. With smol, this
isn't exactly a hassle anymore.
For exiting the program when not using
__libc_start_main, you can just use a
bare syscall, or