深入理解计算机系统之链接浅析(二)

上篇我们提到,为了构造可执行文件,链接器必须完成两个主要任务:符号解析重定位

今天我们就来分别介绍符号解析与重定位

符号解析:

一、链接器解析符号
       

        链接器解析符号引用的方法是将每个引用与它输入的可重定位目标文件的符号表中的一个确定的符号定义联系起来。        

        符号解析的两种情况 :      

        本地符号:对那些和引用定义在相同模块中的本地符号的引用,符号解析是非常简单明了的。编译器只允许每个模块中每个本地符号只有一个定义。        

        全局符号:符号没在当前模块中定义,编译器遇到它时会假设该符号在其他某个模块中定义的,生成一个链接器符号表条                                 目,并把它交给链接器处理。如果链接器在它的任何输入模块中都找不到这个被引用的符号,就输出一条错误                                 信息并终止。                              

                                例:

                                       

         
                                      ​
                                运行后:

                                       
                               ​                              

                           ②多个目标文件可能会定义相同的符号。编译时编译器会通过“强弱符号”来处理多重定义的符号。
                                                                                                                                    (C++和Java可以使用函数重载处理)
二、与静态库链接
     

静态库

        所有的编译系统都提供一种机制,将所有相关的目标模块打包成为一个单独的文件,称为静态库(static library),它可以用做链接器的输入。当链接器构造一个输出的可执行文件时,它只拷贝静态库里被应用程序引用的目标模块,这就减少了可执行文件在磁盘和存储器中的大小,另一方面,程序员只需要包含较少的库文件的名字。

        在Unix系统中,静态库以一种称为存档(archive)的特殊文件格式存放在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合,有一个头部用来描述每个成员目标文件的大小和位置。存档文件名由后缀.a标识。

       对以上的叙述举一个实例:

        假设我们在一个叫做libvector.a的静态库中提供向量例程:

       在使用ar工具创建了这个库之后,我们编写一个应用main.c,它调用addvec库例程。

     (包含头文件vector.h定义了libvector.a中例程的函数模型)

       

       接下来我们通过编译和链接来创建这个可执行文件,链接器的工作原理如下:链接器判定addvec.o定义的addvec符号是被main.c引用的,所以它拷贝addvec.o到可执行文件,因为main.c中没有引用任何的由multvec.o定义的符号,所以链接器就不会拷贝这个模块到可执行文件。链接器还会拷贝libc.a中的printf.o模块,以及许多C运行时系统中的其它模块。

       与静态库链接:

       

   


重定位:

        一旦链接器完成了符号解析这一步,它就把代码中的 每个符号引用 和 确定的一个符号联系起来。此时链接器就知道它的输入目标模块中的代码节和数据节的确切大小。现在就可以开始重定位了,在这个步骤中,将合并输入模块,并为每个符号分配运行时地址。重定位由两步组成:

  • 重定位节和符号定义:在这一步中,链接器将所有相同类型的节合并为同一类型的新的聚合节。例如,来自输入模块的.data节被全部合成一个节,这个节成为输出的可执行目标文件的.data节,然后链接器将运行时存储器地址赋给新的聚合节,赋给输入模块定义的每个节和每个符号。当这一步完成时,程序中的每个指令和全局变量都有唯一运行时存储器的地址了。
  • 重定位节中的符号引用:在这一步中,链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行地址。为了执行这一步,链接器依赖于称为重定位条目的可重定位目标模块中的数据结构。

小结:

        链接可以在编译时由静态编译器完成,也可以在加载时运行由动态链接器来完成。链接器处理称为目标文件的二进制文件,它有三种不同的形式:可重定位的、可执行的、共享的。可重定位的目标文件由静态链接器合成一个可执行的目标文件,它可以加载到存储器中并执行。共享目标文件(共享库)是在运行时由动态链接器链接和加载的,或隐含在调用程序被加载或开始执行时,或者根据需要在程序调用dlopen库的函数时。

        链接器的两个主要任务是符号解析和重定位,符号解析将目标文件中的每个全局符号都绑定到一个唯一的定义,而重定位确定每个符号的最终存储器地址,并修改那些对目标的引用。

         静态链接器是由像GCC这样的编译驱动器调用的,它们将多个可重定位目标文件合并成一个单独的可执行文件。多个目标文件可以定义相同的符号,而链接器用来悄悄地解析这些多重定义的规则可能在用户程序中引发错误。

        多个目标文件可以被连接到一个单独的静态库中,链接器用库来解析其他目标模块中的符号引用。许多链接器通过从左到右的顺序扫描来解析符号引用,这是另一个引起令人迷惑的链接时错误来源。

        加载器将可执行文件的内容映射到存储器,并运行这个程序。链接器还可能生成部分链接的可执行目标文件,这样的文件中有对定义在共享库中的程序和数据的未解析的引用。在加载时,加载器将部分链接的可执行文件映射到存储器,然后调用动态链接器,它通过加载共享库和重定位程序中的引用来完成链接任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值