0%

对链接的思考

程序测试可用于发现bug,从来不曾有一个测试未发现bug。——Edsger W.Dijkstra发现BUG,1972

1.函数库、链接和载入

首先我们说一说编译器,这些内容都和编译器有关。

编译器不是单一的庞大系统,通常由多达六七个稍小的程序所组成,这些程序由一个叫“编译器驱动器”(compiler driver)的控制程序调用。

先来点编译器的流程与组成图:

预处理器主要进行宏处理,文件包含,语言拓展等,就是#开头那些。

编译器的分析阶段也称为前端,它将程序划分为基本的组成部分,检查代码的词法、语义、语法,然后生成中间代码。分析阶段包括词法分析、语义分析和语法分析。

编译器的合成阶段也称为后端,生成目标代码,即代码生成器。

链接-载入器首先将一些目标文件链接起来形成可执行文件,寻找定位程序的参考模块,决定代码被加载到的内存位置,然后加载可执行文件到操作系统并执行,同时计算程序大小,为其创造内存空间,初始化多个寄存器来执行初始化。

链接是将各种代码和数据片段收集并组合成为一个单一的文件过程,这个文件被加载到内存并执行。在现代系统中,链接是由链接器程序(loader)自动执行的。

我们常说的目标文件不能直接执行,首先要到链接器中,链接器确定main函数为初始进入点(程序开始执行的地方),把符号引用绑定到内存地址,把所有目标文件集中在一起,再加上库文件,从而产生可执行文件。

2.静态链接、动态链接

(1)为什么要静态链接

开发时不可能只有一个源文件,对于多个源文件来讲会存在各种依赖关系,但是每一个源文件都是独立编译的,为了满足前面这些依赖关系,则需要对源文件产生的目标文件进行链接,从而形成一个可执行程序,这个过程就是静态链接。

(2)静态链接原理

如果函数库的一份copy是可执行文件的物理组成部分,那么称之为静态链接。可理解为:编译时完成链接。

静态链接并不是将静态库中的所有文件都加到我们的可执行文件中,而是只将我们用到的那部分复制,比如我们的 main 函数中调用了 printf 和 scanf 函数,那么链接器负责将 main.o 、 printf.o 、scanf.o 合并成一个可执行文件。

静态链接做了两件事:符号解析和重定位。

优点:

可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。

缺点:

1.浪费空间:每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,如多个程序中都调用了printf()函数,则这多个程序中都含有printf.o,所以同一个目标文件都在内存存在多个副本,就很浪费。

2.更新困难:每当库函数的代码修改后,就需要重新进行编译链接形成可执行程序。

(3)动态链接原理

动态链接允许系统提供一个庞大的函数库集合,可以提供许多有用的服务。但是程序在运行时寻找他们,而不是把这些函数库的二进制代码作为自身可执行文件的一部分。

可理解为:程序运行时,根据需求再去链接。

如果可执行文件只是包含了文件名,让载入器在运行时能够寻找程序所需要的函数库,就叫动态链接。目前Windows操作系统就采用了动态链接。提到动态链接就必须提到共享库这一概念。

(4)共享库

共享库会被加载到内存中,然后所有的进程需要使用共享库中的内容时,就不用将其加入到程序文件中,而是直接共享它。中间的共享库内存映射区存放的就是共享库的内容,所有进程都通过该部分的内存映射来使用共享库。

3.链接的特殊秘密

1.动态库文件拓展名是“.so”,而静态库文件拓展名是“.a”

2.静态库中的符号提取方法比动态的限制更严格

3.观察头文件来确认使用的函数库

4.a.out与段

a.out是assembler output汇编程序输出的缩写。

这里的段不要与x86架构中的段混淆!UNIX中的段表示一个二进制文件相关的内容块,可以方便映射到链接器在运行时可以直接载入的对象中。从本质上来说,段在正在执行的程序中是一块内存区域,每个区域都有特定的目的。

5.OS在a.out里做的事

BSS段:Block Started by Symbol,通常是指用来存放程序中未初始化的 全局变量 和 静态变量 的一块内存区域。 特点是:可读写的,在程序执行之前BSS段会自动清0。 所以,未初始的全局变量在程序执行之前已经是0了。

数据段:包含经过初始化的全局和静态变量以及他们的值。BSS段的大小可以从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段之后。一般情况下,在任何进程中数据段是最大的段。

文本段:包含程序的指令。链接器把指令直接从文件拷贝到内存中后再也不管了,因为一般情况下程序的文本内容和大小都不会改变。

👋虚拟地址空间最低部分没有被映射,也就是说位于进程地址空间内但是没有赋予物理地址,任何对他的引用都是违法的,典型情况下是从0地址开始的几K字节,用于捕捉使用空指针和小整型值的指针引用内存情况。

-------------本文结束感谢您的阅读-------------
请作者喝一杯蜜雪冰城吧!