Executing main() in C/C++ - behind the scene - GeeksforGeeks

19 Aug, 2020
How to write a C program to print “Hello world” without main() function?At first, it seems impractical to execute a program without a main() function because the main() function is the entry point of any program.
Let us first understand what happens under the hood while executing a C program in Linux system, how main() is called and how to execute a program without main().
Following setup is considered for the demonstration.
Ubuntu 16.4 LTS operating system
GCC 5.4.0 compiler
objdump utility
From C/C++ programming perspective, the program entry point is main() function. From the perspective of program execution, however, it is not. Prior to the point when the execution flow reaches to the main(), calls to few other functions are made, which setup arguments, prepare environment variables for program execution etc.
The executable file created after compiling a C source code is a Executable and Linkable Format (ELF) file.Every ELF file have a ELF header where there is a e_entry field which contains the program memory address from which the execution of executable will start. This memory address point to the _start() function.After loading the program, loader looks for the e_entry field from the ELF file header. Executable and Linkable Format (ELF) is a common standard file format used in UNIX system for executable files, object code, shared libraries, and core dumps.
Let’s see this using an example. I’m creating a example.c file to demonstrate this.
int main(){   return(0);}
Now compiling this using following commands
gcc -o example example.c

Now an example executable is created, let us examine this using objdump utility
objdump -f example

This outputs following critical information of executable on my machine. Have a look at start address below, this is the address pointing to _start() function.
example:     file format elf64-x86-64
architecture: i386:x86-64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00000000004003e0

We can cross check this address by deassembling the executable, the output is long so I’m just pasting the output which shows where this address 0x00000000004003e0 is pointing
objdump --disassemble  example

Output :
00000000004003e0 <_start>:
  4003e0:    31 ed                    xor    %ebp,%ebp
  4003e2:    49 89 d1                 mov    %rdx,%r9
  4003e5:    5e                       pop    %rsi
  4003e6:    48 89 e2                 mov    %rsp,%rdx
  4003e9:    48 83 e4 f0              and    $0xfffffffffffffff0,%rsp
  4003ed:    50                       push   %rax
  4003ee:    54                       push   %rsp
  4003ef:    49 c7 c0 60 05 40 00     mov    $0x400560,%r8
  4003f6:    48 c7 c1 f0 04 40 00     mov    $0x4004f0,%rcx
  4003fd:    48 c7 c7 d6 04 40 00     mov    $0x4004d6,%rdi
  400404:    e8 b7 ff ff ff           callq  4003c0 
  400409:    f4                       hlt    
  40040a:    66 0f 1f 44 00 00        nopw   0x0(%rax,%rax,1)

As we can clearly see this is pointing to the _start() function.
The _start() function prepare the input arguments for another function _libc_start_main() which will be called next. This is prototype of _libc_start_main() function. Here we can see the arguments which were prepared by _start() function.
int __libc_start_main(int (*main) (int, char * *, char * *), /* address of main function*/int argc, /* number of command line args*/char ** ubp_av, /* command line arg array*/void (*init) (void), /* address of init function*/void (*fini) (void), /* address of fini function*/void (*rtld_fini) (void), /* address of dynamic linker fini function */void (* stack_end) /* end of the stack address*/);
The role of _libc_start_main() function is following –
Preparing environment variables for program execution
Calls _init() function which performs initialization before the main() function start.
Register _fini() and _rtld_fini() functions to perform cleanup after program terminatesAfter all the prerequisite actions has been completed, _libc_start_main() calls the main() function.Writing program without main()Now we know how the call to the main() is made.To make it clear, main() is nothing but a agreed term for startup code. We can have any name for startup code it doesn’t necessarily have to be “main”. As _start() function by default calls main(), we have to change it if we want to execute our custom startup code. We can override the _start() function to make it call our custom startup code not main(). Let’s have an example, save it as nomain.c –#include<stdio.h>#include<stdlib.h>void _start(){    int x = my_fun(); //calling custom main function    exit(x);}  int my_fun() // our custom main function{    printf("Hello world!\n");    return 0;}Now we have to force compiler to not use it’s own implementation of _start().In GCC we can do this using -nostartfilesgcc -nostartfiles -o nomain nomain.c
Execute the executable nomain./nomain
Output:Hello world!
Referenceshttp://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.htmlAdvanced C/C++ Compiling by Milan StevanovicThis article is contributed by Atul Kumar. If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.My Personal Notes
arrow_drop_upSave
Now we know how the call to the main() is made.To make it clear, main() is nothing but a agreed term for startup code. We can have any name for startup code it doesn’t necessarily have to be “main”. As _start() function by default calls main(), we have to change it if we want to execute our custom startup code. We can override the _start() function to make it call our custom startup code not main(). Let’s have an example, save it as nomain.c –
#include<stdio.h>#include<stdlib.h>void _start(){    int x = my_fun(); //calling custom main function    exit(x);}  int my_fun() // our custom main function{    printf("Hello world!\n");    return 0;}
Now we have to force compiler to not use it’s own implementation of _start().In GCC we can do this using -nostartfiles
gcc -nostartfiles -o nomain nomain.c

Execute the executable nomain
./nomain

Output:
Hello world!

References
http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html
Advanced C/C++ Compiling by Milan Stevanovic
This article is contributed by Atul Kumar. If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.
nikhilnagrale2
system-programming
C Language
C++
CPP
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
TCP Server-Client implementation in C
Multithreading in C
Exception Handling in C++
Arrow operator -> in C/C++ with Examples
'this' pointer in C++
Vector in C++ STL
Initialize a vector in C++ (6 different ways)
Inheritance in C++
Map in C++ Standard Template Library (STL)
C++ Classes and Objects