链接即将多个可重定位的目标文件(包含代码、数据、编译链接等信息)合并成一个可执行的目标文件的过程。包括静态链接、动态链接、和运行时链接。链接器在软件开发过程中扮演着一个关键的角色,因为它使得分离编译成为可能。
链接过程
假设有文件main.c和swap.c,当我们执行 gcc -g -o p main.c swap.c时,实际上经历了一下编译过程:
1.预处理过程(中间文件)
cpp main.c /tmp/main.i
2.编译过程(汇编代码)
cc1 /tmp/main.i main.c -o /tmp/main.s
3.汇编过程(可重定位目标文件)
as -o /tmp/main.o /tmp/main.s
4.链接过程(可执行文件)
ld -o p /tmp/main.o /tmp/swap.o
编译需要经历预处理、编译、汇编、链接这几个过程,最终得到可执行的二进制文件。
静态链接
程序的编译一般要经历预处理、编译、汇编、链接几个阶段,静态链接发生在链接阶段。在链接阶段,连接器会符号链接、解析多重定义符号、进行静态库链接。
其中在链接时,会将静态库中的部分目标代码取出,拷贝到目标文件中,生成可执行的目标代码。
过程:链接器维持一个目标集合E、未解析符号集合U、已解析符号集合D,初始状态它们都是空的。对于链接器中数据的每一个文件,一个一个输入,相当于迭代的过程来修改集合E、U、D。那么对于其中某个文件f,如果f是一个目标文件,那么将其加入到E中,修改U和D来反映f引用的符号和被引用的符号;如果是一个存档文件,链接器会对存档文件中的每一个目标文件进行前面操作。 当文件f输入处理完之后,接着处理下一个,知道所有的文件被处理完最后得到目标文件。
http://blog.chinaunix.net/uid-26642637-id-3252106.html
关键技术
1.符号解析
将每个引用与它输入的可重定位的目标文件的符号表中的一个确定的符号定义联系起来。
2.重定位
汇编器从0地址开始生成代码和数据段,当将多个目标文件链接到一起时,需要修改代码和数据的位置,并且链接器将符号引用与定义关联起来。
动态链接
解决问题:
1.静态库和所有软件一样需要定期维护,如果需要使用新版本的库,那么需要显示的将原来的程序与新版本的库进行重新链接。
2.对于系统常用的I/O函数,如果静态的复制到进程的文本中,那么当进程较多时显然浪费内存。
而共享库解决了静态库的问题,它发生在运行阶段,程序运行时将动态库拷贝到内存当中,在程序中是可以链接运行的。
共享库是以两种不同的方式“共享”的,首先在给定的文件系统中,对于一个库只有一个.so文件,所有引用该库的可执行目标文件共享这个.so文件中的代码和数据,而不是像静态库那样被拷贝到每一个可执行文件中;其次,在存储器中,一个共享库的文本段的一个副本可以被不同的正在运行的进程共享。
下面是在一个用程序中,使用动态链接库的示例:
与位置无关的代码PIC:
参考 http://wenku.baidu.com/link ,与位置无关的代码,即放到虚拟地址空间中的任何地方,不需要修改就可以运行。其中不使用绝对寻址,都是使用的相对寻址。
关键技术
1.符号解析
将每个引用与它输入的可重定位的目标文件的符号表中的一个确定的符号定义联系起来。
2.重定位
汇编器从0地址开始生成代码和数据段,当将多个目标文件链接到一起时,需要修改代码和数据的位置,并且链接器将符号引用与定义关联起来。