程序员的自我修养 摘录 4.1 静态链接 空间与地址分配 .text .data .bss 全局符号

文章目录

  • 空间与地址分配
    • 按序叠加
      • 一个问题
    • 相似段合并
    • 分配什么空间?
    • 分配空间的步骤:两步链接
    • 链接前后,VMA变化
    • 为什么不从0开始分配内存空间?
    • 符号地址的确定

空间与地址分配

对于链接器而言,整个链接过程中,它就是将几个输入目标文件加工后合并成一个输出文件.那么在这个例子中,我们的输入就是目标文件a.o和b.o.
输出文件就是可执行文件ab.
之前介绍过了ELF文件格式,可执行文件中的代码段与数据段都是由输入文件中合并而来的.那么链接过程就明显产生一个问题,当多个输入文件时,链接器如何把它们的各个段合并到输出文件中?或者说,输出文件中的空间如何分配给输入文件?

按序叠加

一个最简单的方案就是将输入的目标文件按次序叠加,
如下图
程序员的自我修养 摘录 4.1 静态链接 空间与地址分配 .text .data .bss 全局符号_第1张图片

一个问题

这样做的一个问题,就是在有很多输入文件的情况下,输出文件将会有很多零散的段.比如一个规模稍大的应用程序可能会有数百个目标文件,如果每个目标文件都分别有.text段,.data段,.bss段,那最后的输出文件也将会有成百上千个段.
这种做法非常浪费空间,因为每个段都须要有一定的地址与空间对齐的要求.比如对于X86架构的硬件来说,段的装载地址和空间的对齐单位是,也就是4096个字节.
那么就是说,如果一个段的长度只有1个字节,它在内存中也要占用4096个字节.这样会造成内存空间大量的内部碎片.所以这不是一个很好的方案.

相似段合并

一个更加实际的做法是把相同性质的段合并在一起,比如将所有输入文件的.text合并到输出文件的.text段,接着是.data段,.bss段,如下图
程序员的自我修养 摘录 4.1 静态链接 空间与地址分配 .text .data .bss 全局符号_第2张图片
前面提到过,.bss在目标文件中和可执行文件中并不占用文件的空间,但是它在装载时占用地址空间.所以在链接器在合并各个段的同时,也将.bss合并,并且分配虚拟内存.从.bss段的空间分配上可以思考一个问题,那就是这里的所谓的’空间分配’,到底是什么空间?

分配什么空间?

链接器为目标文件分配地址和空间,分配的地址和空间.一个是输出的可执行文件中的空间,第二个是在装载后的虚拟地址中虚拟地址空间.对于有实际数据的段,如.text,它们在文件中和虚拟地址中都要分配空间,因为它们在两者中都存在,而对于.bss段,只需要在虚拟地址空间中分配空间即可.
事实上,我们在这里讨论的空间分配只关注于虚拟地址的空间的分配.因为这关系到链接器后面计算地址的步骤,而可执行文件本身的空间分配与链接过程关系并不是很大.

现在的链接器分配空间的策略基本上采用上述方法的第二种,使用这种方法的链接器一般都采用一种叫做两步链接(two-pass-linking)的方法.也就是说整个链接过程分两步.

分配空间的步骤:两步链接

第一步 空间与地址分配
扫描所有的输入目标文件,并且获得它们的各个段的长度,属性和位置,并且将输入目标文件中的符号表中所有的符号和符号引用收集起来,统一放在一个全局符号表中.这一步中,链接器将能获得所有输入目标文件的段长度,并且将它们合并,计算出输出文件中各个段合并后的长度与位置,并建立映射关系

第二步 符号解析与重定位
使用上面第一步中收集的信息,读取输入文件中段的数据,重定位数据,并且进行符号解析与重定位,调整代码中的地址等.事实上第二步是链接过程的核心,特别是重定位的过程.
将a.o,b.o两个目标文件链接成一个可执行文件后
程序员的自我修养 摘录 4.1 静态链接 空间与地址分配 .text .data .bss 全局符号_第3张图片
VMA即virtual memory address ,即虚拟地址,LMA表示load memory address,即加载地址,正常情况下两个值不一样,但是有些嵌入式系统中,特别是程序放在ROM中的系统时,LMA和VMA是不相同的.这里我们只关注VMA即可.

链接前后,VMA变化

链接前,VMA都为0,因为虚拟空间还未被分配,所以默认都是0.等到链接后,可执行文件ab中的各个段都被分配到了相应的虚拟地址.程序员的自我修养 摘录 4.1 静态链接 空间与地址分配 .text .data .bss 全局符号_第4张图片

为什么不从0开始分配内存空间?

为什么链接器将可执行文件ab的.text代码段分配到了0x80开始的位置,而不是从虚拟空间为0地址开始分配呢?这涉及操作系统进程虚拟地址空间的分配规则,在linux中,ELF可执行文件默认从地址0x8048000开始分配

符号地址的确定

当前一步完成后,链接器开始计算各个符号的虚拟地址,因为各个符号在段内的相对位置是固定的,所以这时候其实main,swap的地址已经是确定的了,只不过链接器必须给每个符号加一个偏移量,使它们能够调整到正确的虚拟地址.
比如a.o中的main函数相对于.text的偏移量是X,但是经过链接合并以后,a.o中的.text段位于虚拟地址0x8048094,那么main地址应该是0x8048094+X.我们可以通过完全一样的计算方法得到所有符号的地址,链接器更新全局符合的地址以后,地址如下图
程序员的自我修养 摘录 4.1 静态链接 空间与地址分配 .text .data .bss 全局符号_第5张图片

你可能感兴趣的:(Methodology,程序员的自我修养,程序员,自我修养,自我提升)