对于我这位初学者而言,链接(linking)这个名词听着既熟悉又陌生。我知道基本上每一次运行都会包含链接操作,但又不知道链接具体又是如何做到的。所以我想理清一下这个内容。
链接(linking)是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并运行。链接可以执行于编译(compile)的时候,也就是在源代码被翻译成机器代码时;也可以执行于加载(load)的时候,也就是有应用程序来执行。早期计算机系统中,链接是手动执行的。现代系统中,链接是由叫做链接器(linker)的程序自动执行的。
无论是什么样的操作系统、ISA或者目标文件格式,基本的链接概念是通用的,细节可能不尽相同,但是概念是相同的。
下面,让我们在linux系统下做一些小测试。
main.c的代码:
int sum(int *a, int n);
int array[2] = {1, 2};
int main()
{
int val = sum(array, 2);
return val;
}
sum.c的代码:
int sum(int *a, int n)
{
int i, s = 0;
for (i = 0; i < n; i++) {
s += a[i];
}
return s;
}
在Linux系统下,输入命令:gcc -Og -o prog main.c sum.c
其中-o 命令能够生成目标文件,prog 是将目标文件重命名
由于main.c中没有打印函数,所以我在main.c文件中加了几行代码让返回结果能够打印输出。结果如下:
main.c 和sum.c文件经过翻译器生成 main.o和sum.o可重定位目标文件。再经过链接器(ld)生成被重命名为prog的完全链接的可执行目标文件。
这个过程可以通过用 -v选项运行gcc查看。
驱动程序首先运行C预处理器(cpp),它将C的源程序main.c翻译成一个ASCⅡ码的中间文件main.i:
cpp main.c /tmp/main.i
然后驱动程序运行C编译器(ccl),它将main.i 翻译成一个ASCⅡ汇编语言文件main.s:
ccl /tmp/main.i -Og -o /tmp/main.s
然后,驱动程序运行汇编器(as),它将main.s翻译成一个可重定位目标文件main.o:
as -o /tmp/main.o /tmp/main.s
驱动程序经过相同的过程生成sum。最后,它运行链接器程序ld,将main.o和sum.o以及一些必要的系统目标文件组合起来,创建一个可执行目标文件prog:
ld -o prog /tmp/main.o /tmp/sum.o
最后再运行可执行文件prog:
./prog
通过上述过程发现,目标文件有多种形式,实际上是有三种:可重定位目标文件、可执行目标文件和共享目标文件。