RANDOM BITS

A random site by a random clueless human
Random bits of programming, math, and thoughts By a clueless human          Random bits of programming, math, and thoughts By a clueless human

Replacing main()

August 24, 2024

micro   gcc   c

Any beginner C programmer will know that the first function executed in any program is the main() function. However, that is not the entire truth. Just like how we have learned the Bohr and Lewis diagrams in Chemistry in Highschool, this is an oversimplification. From my knowledge, the first function executed once the loader runs in a binary is _start().

Without going into any details, we can replace main() with another function such as foo() (sorry for the lack of creativity).

#include <stdio.h>
#include <stdlib.h>

int foo() {
  printf("Called foo\n");
  exit(0);
}

int main() {
  printf("Called main\n");
  return 0;
}

If we compile with -e <entry> where <entry> is the name of the function replacing main(), we can see the following results:

$ gcc foo.c -e foo
$ ./a.out 
Called foo

We can also observe from objdump and nm to see where the start_address of the C code is (here I am making a distinction between the first entry point of the C code and the binary).

$  objdump -f ./a.out | grep start
start address 0x0000000000401136
$ nm ./a.out | grep foo
0000000000401136 T foo

Few Notes

  1. You must define main() even if it’s not going to be used. CPP Reference states this explicitly:

    Every C program coded to run in a hosted execution environment contains the definition (not the prototype) of a function named main, which is the designated start of the program.

    Neglecting to define main results in an error like the following:

    $ gcc foo.c
    /usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crt1.o: in function `_start':
    (.text+0x1b): undefined reference to `main'
    collect2: error: ld returned 1 exit status
    
  2. The C program entry must call exit() to terminate if it is not main() or else a segfault will occur
    $ ./a.out 
    Called foo
    Segmentation fault (core dumped)
    

    a backtrace via gdb won’t give much information as to why. Probably best to consult with glibc. Essentially it is likely due to the fact that _start is not a function that returns in the stack. It calls exit to terminate the program which probably does some cleaning via atexit and set the exit status $? to some value.

    (gdb) bt 
    #0  0x0000000000000001 in ?? ()
    #1  0x00007fffffffdd46 in ?? ()
    #2  0x0000000000000000 in ?? ()
    
  • https://vishalchovatiya.com/posts/crt-run-time-before-starting-main/
  • https://www.gnu.org/software/hurd/glibc/startup.html
  • https://stackoverflow.com/questions/63543127/return-values-in-main-vs-start