若main.c 为c语言文件, gcc -c main.c main.o ;gcc main.o -o main
其中-c位目标文件, -0为可执行文件, -s 为汇编文件;
gcc main.o -o main 相当于
$ ld /usr/lib/crt1.o /usr/lib/crti.o main.o -o main -lc -dynamic-
linker /lib/ld-linux.so.2
也就是说,除了crt1.o之外其实还有crti.o,这两个目标文件和我们的main.o链接在一起生成可执
行文件main 。-lc表示需要链接libc 库,在第 1 节 “数学函数”讲过-lc选项是gcc 默认的,不用写,
而对于ld则不是默认选项,所以要写上。-dynamic-linker /lib/ld-linux.so.2 指定动态链接器
是/lib/ld-linux.so.2 。
那么crt1.o和crti.o里面都有什么呢?我们可以用readelf命令查看。在这里我们只关心符号表,如
果只看符号表,可以用readelf命令的-s选项,也可以用nm命令。
$ nm /usr/lib/crt1.o
00000000 R _IO_stdin_used
00000000 D __data_start
U __libc_csu_fini
U __libc_csu_init
U __libc_start_main
00000000 R _fp_hw
00000000 T _start
00000000 W data_start
U main
$ nm /usr/lib/crti.o
U _GLOBAL_OFFSET_TABLE_
w __gmon_start__
00000000 T _fini
00000000 T _init
U main 这一行表示main 这个符号在crt1.o中用到了,但是没有定义(U表示Undefined),因此需要
别的目标文件提供一个定义并且和crt1.o链接在一起。具体来说,在crt1.o中要用到main 这个符
号所代表的地址,例如有一条指令是push $符号main所代表的地址,但不知道这个地址是多少,所
以在crt1.o中这条指令暂时写成push $0x0 ,等到和main.o链接成可执行文件时就知道这个地址
是多少了,比如是0x80483c4,那么可执行文件main 中的这条指令就被链接器改成了push $0x80483c4 。
链接器在这里起到符号解析(Symbol 件)”我们看到链接器起到重定位的作用,这两种作用都是通过
修改指令中的地址实现的,链接器也是一种编辑器,vi和emacs 编辑的是源文件,而链接器编辑的是
目标文件,所以链接器也叫LinkEditor。T _start这一行表示_start这个符号在crt1.o中提供了定义,
这个符号的类型是代码(T表示Text)。我们从上面的输出结果中选取几个符号用图示说明它们之
间的关系:
图 19.3. C程序的链接过程
其实上面我们写的ld命令做了很多简化,gcc 在链接时还用到了另外几个目标文件,所以上图多画
了一个框,表示组成可执行文件main 的除了main.o、crt1.o和crti.o之外还有其它目标文件。