个人对于“链接”的理解

一、概念:

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

链接器在软件开发中扮演着重要的角色,使得分离编译成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。链接通常是由链接器默默处理的。

二、静态链接

以一组可重定位目标文件(.o)和命令行参数作为输入,生成一个完全链接的、可以加载和运行的可执行目标文件作为输出。

为了构造可执行文件,链接器必须完成两个主要任务:

1、符号解析:目标文件定义和引用符号,每个符号对应于一个函数、一个全局变量或一个静态变量(即C语言中任何以static属性声明的变量)。符号解析的目的是将每个符号引用正好和一个符号定义关联起来。

2、重定位:编译器和汇编器生成从地址0开始的代码和数据节。链接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使得它们指向这个内存位置。

目标文件纯粹是字节块的集合。这些块中,有些包含程序代码,有些包含程序数据,而其它的则包含引导链接器和加载器的数据结构。链接器将这些块连接起来,确定被连接块的运行时位置,并且修改代码和数据块中的各种位置。

三、目标文件

1、可重定位目标文件:包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件。

2、可执行目标文件:包含二进制代码和数据,其形式可以被直接复制到内存并执行。

3、共享目标文件:一种特殊类型的可重定位目标文件,可以在加载或者运行时被动态地加载进内存并链接。

四、符号和符号表

每个可重定位目标模块m都有一个符号表,它包含m定义和引用的符号的信息。在链接器的上下文中,有三种不同的符号:

1、由模块m定义并能够被其他模块引用的全局符号。全局链接器符号对应于非静态的C函数和全局变量。

2、由其他模块定义并被模块m引用的全局符号。这些符号称为外部符号,对应于在其他模块中定义的非静态C函数和全局变量。

3、只被模块m定义和引用的局部符号。它们对应于带static属性的C函数和全局变量。这些符号在模块m中任何位置都可见,但是不能被其他模块引用。

定义为带有C static属性的本地过程变量是不在栈中管理的。相反,编译器在.data或.bss中为每个定义分配空间,并在符号表中创建一个有唯一名字的本地链接器符号。

比如,假设在同一模块中的两个函数各自定义了一个静态局部变量x:

int f()
{
    static int x=0;
    return x;
}


int g()
{
    static int x=1;
    return x;
}

在这种情况中,编译器向汇编器输出两个不同名字的局部链接器符号。比如,它可以用x.1表示函数f中的定义,而用x.2表示函数g中的定义。

注:使用static属性的函数和全局变量不可以在外部模块中被访问。

五、符号解析

链接器解析符号引用的方法是将每个引用与它输入的可重定位目标文件的符号表中的一个确定的符号定义关联起来。编译器只允许每个模块中每个局部符号有一个定义。静态局部变量也会有本地链接器符号,编译器还要确保它们拥有唯一的名字。

汇编器构造符号表,链接器进行符号解析。

1、链接器如何解析多重定义的全局符号?

链接器的输入是一组可重定位目标模块。

函数和已初始化的全局变量是强符号,未初始化的全局变量是弱符号。

规则1:不允许有多个同名的强符号。

规则2:如果有一个强符号和多个弱符号同名,那么选择强符号。

规则3:如果有多个弱符号同名,那么从选择这些弱符号中任意选择一个。

六、静态库

1、概念:所有的编译系统都提供一种机制,将所有相关的目标模块打包成为一个单独的文件,称为静态库,它可以用作链接器的输入。当链接器构造一个输出的可执行文件时,它只复制静态库里被应用程序引用的目标模块。
2、不使用静态库,编译器开发人员会用什么方法来向用户提供这些函数?

(1)让编译器辨认出对标准函数的调用,并直接生成相应的代码。

(2)将所有的标准C函数都放在一个单独的可重定位目标模块中,可以把这个模块链接到他们的可执行文件中。

这种方法的优点是:它将编译器的实现与标准函数的实现分离开来,并且仍然对程序员保持适度的便利。

但缺点是:(1)系统中每个可执行文件现在都包含着一份标准函数集合的完全副本,这对磁盘空间是很大的浪费。

(2)对任何标准函数的任何使用或者改变,无论多小的改变,都要求库的开发人员重新编译整个源文件。

静态库概念被提出来,解决这些不同方法的缺点。相关的函数可以被编译为独立的目标模块,然后封装成一个单独的静态库文件。然后,应用程序可以通过在命令行上指定单独的文件名字来使用这些在库中定义的函数。

比如,使用C标准库和数学库中函数的程序可以用以下形式的命令行来编译和链接:

gcc main.c /usr/lib/libm.a /usr/lib/libc/a

在链接时,链接器将只复制被程序引用的目标模块,这就减少了可执行文件在磁盘和内存中的大小。另一方面,应用程序员只需要包含较少的库文件的名字。

静态库以一种称为存档的特殊文件格式存放在磁盘中。

七、重定位

当汇编器生成一个目标模块时,它并不知道数据和代码最终将放在内存中的什么位置。它也不知道这个模块引用的任何外部定义的函数或者全局变量的位置。所以,无论何时汇编器遇到对最终位置未知的目标引用,它就会生成一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用。

八、动态链接共享库

静态库和所有的软件一样,需要定期维护和更新。

另一个问题是几乎每个C程序都使用标准I/O函数,比如printf和scanf。在运行时,这些函数的代码会被复制到每个运行进程的文本段中。在一个运行上百个进程的典型系统上,这将是对稀缺的内存系统资源的极大浪费。

共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并和一个在内存中的程序链接起来。这个过程称为动态链接,是由一个叫做动态链接器的程序来执行的。共享库也称为共享目标,微软的操作系统大量的使用共享库,它们称为DLL(动态链接库)。

共享库是以两种不同的方式来“共享”的。首先,在任何给定的文件系统中,对于一个库只有一个.so文件。所有引用该库的可执行目标文件共享这个.so文件中的代码和数据,而不是像静态库的内容那样被复制和嵌入到引用它们的可执行的文件中。其次在内存中,一个共享库的.text节的一个副本可以被不同的正在运行的进程共享。

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值