t-linux)
compiled by GNU C version 4.4.5 20101112 (Red Hat 4.4.5-2), GMP version 4.3.1, MPFR version 2.4.2.
GGC heuristics: --param ggc-min-expand=81 --param ggc-min-heapsize=95788
Compiler executable checksum: e892644090a9a7e8c330a388c51818dd
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'
as -V -Qy -o /tmp/cc1w7Hxi.o /tmp/ccrwAICf.s
GNU assembler version 2.20.51.0.2 (i686-redhat-linux) using BFD version version 2.20.51.0.2-15.fc13 20091009
COMPILER_PATH=/usr/libexec/gcc/i686-redhat-linux/4.4.5/:/usr/libexec/gcc/i686-redhat-linux/4.4.5/:/usr/libexec/gcc/i686-redhat-linux/:/usr/lib/gcc/i686-redhat-linux/4.4.5/:/usr/lib/gcc/i686-redhat-linux/:/usr/libexec/gcc/i686-redhat-linux/4.4.5/:/usr/libexec/gcc/i686-redhat-linux/:/usr/lib/gcc/i686-redhat-linux/4.4.5/:/usr/lib/gcc/i686-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/i686-redhat-linux/4.4.5/:/usr/lib/gcc/i686-redhat-linux/4.4.5/:/usr/lib/gcc/i686-redhat-linux/4.4.5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=i686'
/usr/libexec/gcc/i686-redhat-linux/4.4.5/collect2 --no-add-needed --eh-frame-hdr --build-id -m elf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc/i686-redhat-linux/4.4.5/../../../crt1.o /usr/lib/gcc/i686-redhat-linux/4.4.5/../../../crti.o /usr/lib/gcc/i686-redhat-linux/4.4.5/crtbegin.o -L/usr/lib/gcc/i686-redhat-linux/4.4.5 -L/usr/lib/gcc/i686-redhat-linux/4.4.5 -L/usr/lib/gcc/i686-redhat-linux/4.4.5/../../.. /tmp/cc1w7Hxi.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i686-redhat-linux/4.4.5/crtend.o /usr/lib/gcc/i686-redhat-linux/4.4.5/../../../crtn.o
复制代码
注意红色加粗部分(由绿色文件生成红色文件)——
(1)cc1是GCC编译环境中的C编译器,把C代码编译为汇编代码,输出为.s文件
(2)as是汇编器,把汇编代码编译为目标文件,输出为.o文件
(3)collect2是GCC后期版本使用的链接器(环境),其实是先调用GNU的链接器ld对目标文件进行链接,最后收集与程序初始化相关的信息,构造程序的初始化结构。
ld是真正的链接器,对上一步的.o目标文件和其它需要.o文件或静态链接库.a文件、动态链接库.so文件(如解压C标准库libc.a中取出需要的printf.o文件),一起链接输出为a.out文件。
GCC后期版本使用了collect2来作为链接器,其实是间接调用ld链接器。
上面用到的工具中,as是GNU自带的汇编器,ld是GNU自带的链接器,它俩是GNU Binutils中最主要的二进制工具。
其中,ld-linux.so.2是动态链接器。最后注意-lc参数,l表示链接,c表示标准C库,即libc.a或libc.so。
让我们回到问题本身——
main不是
C语言的关键字,但却是约定俗成的主函数名字,不过它并不是程序执行的入口,
C程序真正入口是_start全局符号(由汇编实现的函数),_start函数会调用库函数__libc_start_main,然后__libc_start_main再调用main函数
我们知道main函数的声明无非两种形式,main函数的声明(main符号)其实是在crt1.o目标文件中
通过nm工具可以查看crt1.o包括了哪些符号
复制代码
[zhanghaiba@Fedora code]$ nm /usr/lib/crt1.o
00000000 R _IO_stdin_used
00000004 D __data_start
U __libc_csu_fini
U __libc_csu_init
U __libc_start_main
00000044 R _fp_hw
00000020 T _start
00000004 W data_start
U main
复制代码
crt1.o中已经有了main符号,但却是未定义(U)的,所以需要我们来实现main函数(即定义main符号),最后通过链接器来链接(这里称作符号解析)
如果把main函数定义为static,也就是具有内部链接(Internel Linkage)属性,则编译后的目标文件是局部符号(当前文件可见)
然而链接是不会对局部符号做符号解析的,只会根据目标文件的.rel.text段来指示链接全局的且未定义的符号(即修改可重定位目标文件REL的符号地址)
因此,链接时main符号找不到定义,这导致main符号找不到具体实现(定义),造成链接失败
我们再看编译失败的反馈信息
/usr/lib/gcc/i686-redhat-linux/4.4.5/../../../c