The C runtime

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 crt*.o. This function:

  1. aligns the stack
  2. reads argc, argv, environ and the auxiliary vector from the stack
  3. sets up TLS, locales, and other crap
  4. calls __libc_start_user, which first calls the constructors of all global (C++) variables, then calls main, and exit automatically 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 main. Of course, the second step can be skipped as well when you don't need args/environ.

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 argc, argv, envp and 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 int3.