前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >链接器做了什么?

链接器做了什么?

作者头像
233333
发布2018-04-13 18:03:38
9810
发布2018-04-13 18:03:38
举报
文章被收录于专栏:linux驱动个人学习

摘自《程序员自我修养》


机器指令的历史

为了更好的理解计算机程序的编译和链接的过程,我们简单地回顾计算机程序开发的历史一定会非常有益。 最原始的设备是就是纸带,即在纸带上打相应的孔。 这个过程我们可以通过图2-6来看到,假设有一种计算机,它的每条指令是一个字节,也就是8位。它的高4位是0001,表示这是一种跳转指令,低4位存放的是跳转目的地的绝对地址。我们可以从图2-6中看到,这个程序的第一条指令就是一条跳转指令,它的目的地址是第5条指令(注意,第5条指令的绝对地址是4).至于0和1怎么映射到纸带上,这个应该很容易理解,比如我们可以规定纸带上每行有8个孔位,每个孔位代表一位,穿孔表示0,未穿孔表示1。

链接的原因

在一个程序被分割为多个模块以后,这些模块之间最后如何组合形成一个单一的程序是须要解决的问题。模块之间如何组合的问题可以归结为模块之间如何通信的问题,最常见的属于静态语言的C、C++之间通信的方式,一种是模块之间的函数调用,另外一种是模块间的变量访问。函数访问须知道目标函数的地址,变量访问也须知道目标变量的地址,所以这两种方式都可以归结为一种方式,那就是模块之间的符号引用。模块间依靠符号来通信类似于拼图版,定义符号的模块多出一个区域,引用该符号的模块刚好烧录那一块区域,两者的拼接刚好完美组合。这个模块组合的过程就是链接。

静态链接

链接过程主要包括了地址和空间分配符号决议重定位等这些步骤。

符号决议有时候也被叫做符号绑定(Symbol Binding)、名称绑定(Name Binding)。甚至还有叫做地址绑定(Address Binding)、指令绑定(Instruction Binding)的,大体上它们的意思都一样,但从细节角度来区分,它们之间还是存在一定区别的,比如“决议”更倾向于静态链接,而“绑定”更倾向于动态链接,即它们所使用的范围不一样。在静态链接中,我们统一称为“符号决议”。

最基本的静态链接过程如图2-8所示。编译过程如下图:

现代编译和链接过程并非想象那么复杂,它还是一个容易理解的概念,比如我们在程序模块main.c使用另外一个模块func.c中的函数foo()。我们在main.c模块中每一处调用的foo的时候都必须确切知道foo函数的地址,所以它暂时把这些调用foo的指令的目标地址搁置,等待最后链接的时候由链接器去将这些指令的目标地址进行修正,则填入正确的foo函数地址。当func.c模块重新编译,foo函数的地址有可能改变时,那么我们在main.c中所有使用到foo的地址的指令将要全部重新调整。这些繁琐的工作将成为程序员的噩梦。使用链接器,你可以直接引用其他模块的函数和全局变量而无需知道它们的地址,因为链接器,你可以直接引用其他模块的函数和全局变量而无须知道它们的地址,因为链接器在链接的时候会根据引用的符号foo,自动去相应的func.c模块查找foo的地址,然后将main.c模块中所有引用到foo的指令重新修正,让它们的目标地址为真正的foo函数的地址。这就是静态链接的最基本功能和作用。 在链接过程中,对其他定义在目标文件中的函数调用的指令须要被重新调整,对使用其他定义在其他目标文件的变量来说,也存在同样的问题。让我们结合具体CPU指令来理解这个过程,假设我们有个全局变量,比如我们在目标文件中B里面有一个指令: movl $0x2a, var 这条指令就是给这个var变量赋值0x2a,相当于C语言中的语句var=42,然后我们编译目标文件B,得到这条指令机器码,如图2-9所示:

由于在编译目标文件B的时候,编译器并不知道变量var的目标地址,所以编译器在无法确定地址的情况下,将这条mov指令的目标地址置为0,等待链接器在将目标文件A和B链接起来的时候再将其修正。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-04-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 机器指令的历史
  • 链接的原因
  • 静态链接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档