zoukankan      html  css  js  c++  java
  • 《程序员的自我修养》 -- 读书笔记4

    4.1 空间地址分配

    链接过程,就是将几个目标文件加工后合并成一个输出文件。那么,对于多个输入目标文件,链接器如何将它们的各个段合并到输出文件呢?

    方法一:按序叠加

    就是将各个目标文件直接依次合并,但是这样做会产生一个问题:当目标文件很多的情况下,会产生大量零散的段,因为每个段都必须要有一定的地址和空间对齐要求(比如对于x86硬件来说,段的装载地址和空间的对齐单位是页,也就是4096字节),因此这种做法非常浪费空间。

    方法二:相似段合并

    就是将同样的段放在一起,每个目标文件的.text段合并,接着是.data段,.bss段等。

    我们都知道,.bss段在目标文件和可执行文件时并不占用文件的空间,但是装载时占用地址空间,因此链接器要将.bss段合并,并且分配虚拟空间。

        这里的“空间分配”到底是什么空间?

        还有文件空间和地址空间有什么不同?     

        “链接器为目标文件分配地址和空间”,“地址和空间”有两个含义:一时在输出的可执行文件中占用的空间,也就是文件空间。而是装载后的虚拟地址中的虚拟地址空间,也就是地址空间。

        现在的链接器空间分配都采用相似段合并的方法,一般都使用一种叫两步链接的方法:

    第一步:空间与地址分配。扫描所有的目标文件,获得各个段的长度和位置,收集各目标文件中的符号表中的符号定义和符号引用,放到一个全局符号表。

    第二步:符号解析与定位。使用步骤一中收集到的所有信息,读取输入文件中段的数据、重定位信息,并且进行符号解析和重定位、调整代码中的地址。这一步是链接的核心,尤其是重定位过程。

    举例:

    a.c

    extern int shared;

    int main()
    {
    int a = 100;
    swap (&a, &shared);
    }

    b.c

    int shared = 1;

    void swap (int* a, int* b)
    {
    *a ^= *b ^= *a ^= *b;
    }

    如上,将a.c和b.c生成的目标文件a.o和b.o链接。

    如上所示,分别是a.o   b.o  ab的段信息。首先,VMA表示虚拟地址,LMA表示加载地址,正常情况下这两个值应该是一样的。

        我们可以看到,在加载之前,VMA的值都为0,等到链接之后,可执行文件ab中的各个段都被分配到了相应的虚拟地址。

    4.2 符号解析与重定位

    1、重定位

    上图是a.o文件的反汇编结果,我们可以看到,因为是虚拟地址,因此main的起始地址为0x0000 0000 0000 0000开始,a.0只定义了一个函数main,这个函数占0x26个字节,共有11条指令。另外,偏移为0x20的指令,是调用swap函数的指令,e8位操作码,后面的四位是swap函数的地址。因为此时还未分配地址,所以暂用00 00 00 00 代替。

        上图是可执行文件ab的反汇编结果,可以看到swap函数已经被分配了地址。(Call指令是一条近址相对位移调用指令,后面跟的是调用指令的下一条指令的偏移量,在这里,下一条指令是leave,地址为0x40010d,加上偏移量3,结果是0x400110,刚好是swap函数的地址。)

    2、重定位表

    ELF文件中有重定位表,用来描述如何修改相应的段里的内容。

        上图是a.o文件的重定位信息,即其所有引用到外部符号的地址。每个要被重定位的地方叫一个重定位入口;重定位入口的偏移,表示该入口在要被重定位的段中的位置;RELOCATION RECORDS FOR [.text]表示这个重定位表是用于代码段的。偏移0x14和0x21分别就是代码段中mov指令和call指令的地址部分。

    3、符号解析

    在写程序时,我们经常会碰到链接是符号为定义的问题。如上如所示,在未链接时,shared和swap都是“UND”,表示未定义类型,这种未定义的符号都是因为该目标文件中有关于它们的重定位项,所以在链接器扫描完所有的目标文件之后,这些未定义的符号都应该能够在全局符号表中找到,否则链接器就会报符号未定义错误。

     4.5 静态库链接

        静态链接可以简单看成是一组目标文件的集合。

        比如在C语言的运行库中,包含了很多跟系统功能相关的代码,编译完成之后有很多目标文件,比如printf.o,scanf.o, date.o, time.o…… 把这些零散的目标文件直接提供给库的使用者,很大程度上会造成文件传输管理不便,因此通常使用“ar”压缩程序将这些目标文件压缩在一起。我们也可以使用“ar”工具来查看一个库包含了哪些目标文件。

    ar –t *.a

  • 相关阅读:
    为什么单个TCP连接很难占满带宽
    上传NUnit的单元测试结果和OpenCover的单元测试覆盖率到SonarQube服务中
    使用Visual Studio Code Coverage和nunit上传单元测试覆盖率和单元测试结果到SonarQube上
    java安装1.8的经验和Error: Registry key 'SoftwareJavaSoftJava Runtime Environment'CurrentVers问题处理
    NSubstitute.Analyzers检测NSubstitute用法冲突
    在TeamCity中执行gtest单元测试
    iOS OpenGL ES入门
    iOS 基础知识
    【内推】字节跳动-头条小说&番茄小说
    iOS开发小记(十四)
  • 原文地址:https://www.cnblogs.com/songshuguiyu/p/8516325.html
Copyright © 2011-2022 走看看