gcc的编译过程分为三步:
第一步 将 *.c 文件分别通过编译器解析成汇编语言 *.s 。
第二步将 *.s 文件分别通过汇编器生产目标文件 *.o 。
第三步将 c.o文件通过链接器合成一个 out 的可执行文件 。
当执行.out 或者 .exe 可执行文件时,程序入口通常是main 函数。一个程序中,必须要有一个入口函数来告诉链接器指明代码从哪里开始执行。大部分连接器的默认函数入口都是main函数。当然你也可以告诉链接器,入口函数是不是main函数,而是其他函数。这个函数名字只要你愿意,改什么都可以,只需要在链接的时候告诉链接器就可以。所以C程序包括其他高级语言的程序main函数并不都是必须的,只不过是链接器的默认指明的程序入口是main而已。
如下图所示
-nostartfiles 链接的时候不使用标准系统的启动文件。
C的标准系统启动文件对应的库叫C run-time library 简称 CRT,C运行时库 ,对应的目标文件是 crt1.o 和crti.o 。 其实正真的程序入口是crt1.o中的_start 。_start中调用了一个叫main的函数。这里的符号表里会发现一个main符号是未定义的,意味者自身文件没有main这个符号,需要从其他文件来提供main。crt1.o和 我们自己写的代码文件链接的时候,就会去找缺失的符号定义。如果未找到,就会报未找到对应标识的错误。
@ readelf -s crt1.o 可以查看符号表,其中信息如下所示,可以看见在13行中 有一个 未被定义的全局变量main ,这个main就需要由用户程序来提供。
Symbol table '.symtab' contains 19 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 SECTION LOCAL DEFAULT 3
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 5
5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
6: 0000000000000000 0 SECTION LOCAL DEFAULT 8
7: 0000000000000000 0 SECTION LOCAL DEFAULT 9
8: 0000000000000000 0 SECTION LOCAL DEFAULT 10
9: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND __libc_csu_fini
10: 0000000000000030 5 FUNC GLOBAL HIDDEN 3 _dl_relocate_static_pie
11: 0000000000000000 47 FUNC GLOBAL DEFAULT 3 _start
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND __libc_csu_init
13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND main
14: 0000000000000000 0 NOTYPE WEAK DEFAULT 8 data_start
15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
16: 0000000000000000 4 OBJECT GLOBAL DEFAULT 5 _IO_stdin_used
17: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND __libc_start_main
18: 0000000000000000 0 NOTYPE GLOBAL DEFAULT 8 __data_start
C run-time library里面含有初始化代码,如果选择不用crt去链接的话,那么你crt对应的程序初始化功能也就没有了。