关于链接的较为全面的介绍
作者:互联网
链接就是将不同部分的代码和数据合成一个单一文件的过程,这个文件可以被加载器加载到存储器执行。链接可以执行于编译时,加载时,甚至运行时。链接器就是起到这样一个作用。
那么链接到底有什么作用呢?
首先,它可以避免构造大型程序时写一个超级大的源文件,我们可以分成更小的模块进行编写,修改某一个模块只需要这个模块重新链接到应用上,其他模块不用重新编译。
其次,它可以帮助我们理解一些编程中的链接错误,全局变量局部变量意义,其他系统概念,开发共享库等。
1.编译器驱动程序
我们看这个实例程序,
首先预处理器将会把main.c翻译成ASCII码中间文件main.i,
然后编译器将把这个main.i转换成汇编语言文件main.s。
接着汇编器把Main.s转换成可重定位目标文件main.o
最后,链接器会把这两个main.o swap.o以及一些必要的文件结合起来形成可执行的目标文件exe
这就是一个程序写出来到运行经过的过程。
我们需要知道目标文件o有三种形式:
1、可重定位目标文件。包括二进制代码和数据,代码,初始化的数据,未初始化的数据不在一个数据节中;
2、可执行目标文件。包括代码和数据,可以直接拷贝到存储器执行。
3、共享目标文件。特殊的可重定位目标文件,可以在加载或运行时动态加载到存储器并链接。
最后一步链接器必须要完成两个任务:
1、符号解析。将每个符号引用和符号定义联系起来;
2、重定位。链接器通过把每个符号定义和一个存储器位置联系起来,然后修改对这些符号的引用,使他们指向这个存储器位置,从而重定位这些节。
2、符号表
符号表是一个结构数组,包含符号偏移量,长度,类型等信息;每个可重定位模块中都有符号表,符号表主要有三种不同的符号:
1、由该模块定义的全局符号,也就是对应于函数和全局变量。
2、由其他模块定义的全局符号,对应于extern关键字,代表着本模块只是声明,定义是其他模块定义的、
3、局部符号,带static属性的函数和变量。
这里要注意,局部符号不是局部变量(非static)。因为局部变量在运行时被栈管理,符号表不关心这些。
其实我们可以发现,static的功能和c++中的private一样,声明了static后其他模块是不能访问的。
3、符号解析
符号为函数名时,指代码所在区,为变量名时,为所占的静态数据区。
对于本地符号的解析十分简单,编译器只允许每个本地符号只有一个定义。而全局变量的解析却复杂得多。
为了解决全局符号多处定义的问题。编译器分成了强符号和弱符号
函数和已经初始化的全局变量是强符号,未初始化的全局变量是弱符号。
规则1:不允许有多个强符号;
规则2:如果有一个强符号和多个弱符号,选择强符号;
规则3:如果有多个弱符号,随便选择一个;
4、静态库链接
所有编译系统都提供一种机制,把所有的相关目标模块打包成一个单独的文件,这就是静态库。标准I/O,串操作,算术函数等存放在libc.a,libm.a库文件中。
如果不使用静态库的话,可以使用标准函数,编译器可以辨认对这些标准函数的调用生成相应代码。比如pascal就是用这种方式,但是C不可行,因为标准函数太多了,每次增删改函数都要一个新的编译器版本。
另一种办法是把所有的标准C函数放在一个单独的可重定位文件中,这样可以使编译器实现和标准函数实现分离开来。这样的话意味着每个可执行文件都将包含一份标准函数完全集合的拷贝,浪费存储器空间。更糟的是,对于任何标准函数的改变,库的开发人员都要重新编译整个源文件,维护变得困难。
而静态库可以解决这些缺点,链接器只会拷贝相关的模块,减少空间浪费。
5、重定位
在这个步骤中,将合并输入模块,并为每个符号分配运行时地址。有两步:
1.重定位节和符号定义。将相同类型的节合并成一个新的聚合节,然后将运行时存储器地址赋给聚合节。这一步完成后,程序的每个指令和全局变量都有唯一的运行时地址了。
2、重定位节中的符号引用。修改代码节和数据节的符号引用,使他们指向正确的运行时地址。这一步依赖于重定位表目的数据结构。
5.加载可执行目标文件
加载器可以将可执行目标文件中的数据和代码从磁盘拷贝到存储器中,然后跳转到程序的第一条指令(入口点),来运行该程序
6、动态链接共享库
静态库的缺点是仍然需要定期维护更新,更新后程序员要显式地将程序和最新的库链接,且对于输入输出这种每个程序都有的函数,如果有100个进程同时进行,这将是对存储器资源的浪费。而动态库就是解决这个缺陷的。
共享库是在加载或运行时加载到任意的存储器地址,并和一个程序链接起来。这个过程叫做动态链接,是由一个动态链接器的程序执行的。
在微软中,共享库称为DLL,动态链接库。
在现实中有一些例子:
1、分发软件,微软经常用共享库来分发软件更新。生成一个共享库的新版本,用户可以下载,代替旧版本。下一次运行程序时就会自动链接新的共享库。
2、构建高性能web服务器 。早期的网站创建子进程,在该上下文中运行CGI程序,生成动态内容。而高性能服务器将生成动态内容的函数打包在一个共享库中,当浏览器请求到达时动态加载链接,直接调用该函数,一个简单的函数调用开销就足够了。
标签:文件,函数,符号,较为,模块,全面,链接,加载 来源: https://blog.csdn.net/weixin_53344209/article/details/119009523