符号是链接中的粘合剂,整个链接过程正是基于符号才能够正确完成,链接过程中很关键的一部分就是符号的管理,每一个目标文件都会有一个相应的富豪榜,这个表里面记录了目标文件中所用到的所有符号,每个定义的符号有一个对应的值,叫做符号值,对于变量和函数来说,符号值就是他们的地址。
在本目标中引用的全局符号,却没有定义在本目标文件,这一般叫做外部符号,也就是我们常说的符号引用。
定义在本文件中的全局符号,可以被其他目标文件引用。
C语言符号修饰:为了防止符号名冲突,UNIX下的C规定,C语言源代码文件中的所有全局变量和函数经过编译后,相对应的符号前加下划线_。
对于链接器来说,整个链接过程,就是将几个输入目标文件加工后合并成一个输出文件。
一般把符号修饰标准、变量内存分布、函数调用方式等这些跟可执行代码二进制兼容性相关的内容称为ABI,Application Binary Interface.
API和ABI都是所谓的应用程序接口,只是他们所描述的接口所在的层面不一样。API往往是指源代码级别的接口,而ABI指的是二进制层面的接口,ABI的兼容成粗比API更为严格,比如我们可以说C++的对象内存分布是C++ ABI的一部分。API更关注与源代码层面。
一个静态库可以看作是一组目标文件的集合,即很多目标文件经过压缩打包后形成的一个文件。比如在Linux中最常用的C语言静态库libc位于/usr/lib/libc.a,它属于glibc项目的一部分,而Windows这样的平台,最常使用的C语言库是由集成开发环境所附带的运行库,这些库一般由编译器厂商提供,比如VC++附带了多个版本的运行库,msvcrt.lib。
每个程序被运行起来以后,它将拥有自己独立的虚拟地址空间。
在32位平台下的4GB虚拟地址空间,我们的程序是否可以任意使用呢?很遗憾,不行。因为程序在在运行时候处于操作系统的监管之下,进程只能使用那些操作系统分配给进程的地址,如果访问未经允许的空间,那么操作系统会捕获这些异常。整个4GB空间被划分成了两部分,其中操作系统本身用去了1GB,剩下的3GB的空间都是留给进程使用的。从原则上来讲,我们的进程最多可以使用3GB的虚拟空间,也就是说在整个进程执行的时,所有的代码、数据包括C语言malloc等方法申请的空间之后不可以超过3GB。3GB空间“原则上”是可以给进程使用的,但令人遗憾的是,进程并不能完全使用这3GB的虚拟空间,其中有一部分是预留给其他用途的。
由于可执行文件在装载时实际上是被映射的虚拟空间,所以可执行文件很多时候又被叫做映像文件image.
静态连接使得不同的程序开发者和部门能够相对独立的开发和测试自己的程序模块。但是静态链接浪费内存和磁盘空间、模块更新困难。
静态链接方式对于计算机内存和磁盘的空间浪费十分严重,特别是多进程操作系统的情况下。
要解决空间浪费和更新困难这两个问题最简单的方法就是把程序的模块相互分隔开来,形成独立的文件,而不再将他们静态链接在仪器,简单的说,就是不对那些组成程序的目标文件进行链接,等到程序要运行时才进行链接。也就是说,把链接这个过程推迟到了运行时再进行,这就是动态链接的基本思想。
动态链接的基本思想是把程序按照模块拆分之后分成各个独立部分,在程序运行时才将他们链接在一起形成一个完整的程序,而不是静态链接那样把所有的程序模块都链接成一个独立的可执行文件。
在Linux中,常用的C语言库的运行库为glibc,它的动态链接形式的版本保存在/libc目录下,文件名叫做libc.so,整个系统只保留 一份C语言库的动态链接文件libc.so,而所有C语言编写的、动态链接的呈现粗都可以在运行时使用它。当程序被装载的时候,系统的动态链接器会将程序所需要的所有动态连接口libc.so装载到进程的地址空间,并且将程序中所有未决议的符号绑定到相应的动态链接库中,并进行重定位工作。