C language without the main function of the helloworld example
- 2020-04-02 03:01:25
- OfStack
Almost every programmer's first class is helloworld, so let's take a look at the classic C language, helloworl
#include <stdio.h>
int main()
{
printf("hello world!n");
return 0;
}
This too is a simple program, but it contains the most important part of a program, that is what we can be seen in almost all of the code of the main function, we compiled into executable file and view the symbol table, filter out the inside of the function is as follows (in order to facilitate check my manually adjust the grep output format, and so your output format is not the same)
$ gcc hello.c -o hello
$ readelf -s hello | grep FUNC
Num: Value Size Type Bind Vis Ndx Name
27: 000000000040040c 0 FUNC LOCAL DEFAULT 13 call_gmon_start
32: 0000000000400430 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
35: 00000000004004a0 0 FUNC LOCAL DEFAULT 13 frame_dummy
40: 0000000000400580 0 FUNC LOCAL DEFAULT 13 __do_global_ctors_aux
47: 00000000004004e0 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
48: 00000000004003e0 0 FUNC GLOBAL DEFAULT 13 _start
51: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
52: 00000000004005b8 0 FUNC GLOBAL DEFAULT 14 _fini
53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
58: 00000000004004f0 137 FUNC GLOBAL DEFAULT 13 __libc_csu_init
62: 00000000004004c4 21 FUNC GLOBAL DEFAULT 13 main
63: 0000000000400390 0 FUNC GLOBAL DEFAULT 11 _init
We all know that the user's code starts with the main function. Although we only wrote a main function, we can see from the above table that there are many other functions, such as _start. In fact, the real entry of the program is not the main function, we compile the hello.c code with the following command
$ gcc hello.c -nostdlib
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400144
The -nostdlib command does not link to the standard library and fails to find the entry symbol _start. In this case, the entry symbol _start cannot be found
In fact, the main function is the entry of the user code, it will go to call the system library, before the main function, system libraries to do some initialization, such as the allocation of global variables to memory, initialization stack, thread, etc., when the main function after the execution, through the exit () function to do some cleanup, users can realize _start function
#include <stdio.h>
#include <stdlib.h>
_start(void)
{
printf("hello world!n");
exit(0);
}
Execute the following compile command and run
$ gcc hello_start.c -nostartfiles -o hello_start
$ ./hello_start
hello world!
The function of -nostartfiles here is Do not use the standard system startup files when linking, that is, Do not use the standard startup files, but still link to the system library, so the program can still be executed. Again, we look at the symbol table
$ readelf -s hello_start | grep FUNC
Num: Value Size Type Bind Vis Ndx Name
20: 0000000000400350 24 FUNC GLOBAL DEFAULT 10 _start
21: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
22: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@@GLIBC_2.2.5
Now there are only three functions left, all of them implemented by ourselves, in which printf is turned off at compile time by adding the -fno-builtin option because only one argument is optimized by the compiler to the puts function
If we remove the exit(0) statement from the _start function, the program will get out of the core, because the _start function will finish the program, and our implementation of _start does not call exit() to clean up memory
Finally removed the main function, then found that there must be a _start function, is not very annoying, in fact, _start function is just a default entry, we can specify the entry
#include <stdio.h>
#include <stdlib.h>
int nomain()
{
printf("hello world!n");
exit(0);
}
Compile with the following command
$ gcc hello_nomain.c -nostartfiles -e nomain -o hello_nomain
Where the -e option specifies the program entry symbol, see the symbol table below
$ readelf -s hello_nomain | grep FUNC
Num: Value Size Type Bind Vis Ndx Name
20: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
21: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@@GLIBC_2.2.5
22: 0000000000400350 24 FUNC GLOBAL DEFAULT 10 nomain
Comparing the symbol table of hello_start, we found that we just changed _start to nomain
So far it's clear that the default entry is the _start function in the standard library, which does some initialization, calls the user's main function, and finally does some cleanup. We can write the _start function to override the _start function in the standard library, or even specify the entry of the program