彻底理解链接器

ps:本文转载自(确切来说,是关于其中4篇链接器博文的整理)
https://blog.csdn.net/github_37382319/article/details/82749205?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-4.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-4.control

什么是链接器(Linker)

  首先是链接器的本质,链接器本质上也是一个程序,本质上和我经常使用的普通程序没什么不同。
   最后是链接器的输出,链接器在将目标文件打包处理后,生成或者可执行文件或库等。
  链接器的作用有点类似于我们经常使用的压缩软WinRAR(Linux下是tar),压缩软件将一堆文件打包压缩成一个压缩文件,而链接器和压缩软件的区别在于链接器是将多个目标文件打包成一个文件而不进行压缩

链接器的工作过程

  首先,链接器对给定的目标文件或库的集合进行符号决议以确保模块间的依赖是正确的。
  其次,链接器将给定的目标文件集合进行拼接打包成需要的库或最终可执行文件。
  最后,链接器对链接好的库或可执行文件进行重定位。

符号决议

  符号决议有时候也被叫做符号绑定,名称决议;决议更倾向于静态链接,而绑定更倾向于动态链接。在这个过程当中,链接器需要做的工作就是确保所有目标文件中的符号引用都有唯一的定义。

目标文件里有什么

  • 代码部分:指的是计算机可以执行的机器指令,也就是源文件中定义的所有函数。
  • 数据部分:源文件中定义的全局变量。

那为什么局部变量没有放到目标文件的数据段当中呢?

  这是因为局部变量是函数私有的,局部变量只能在该函数内部使用,所以函数私有的局部变量被放在了代码段中,作为机器指令的操作数。
  编译器在遇到外部定义的全局变量或者函数时只要能在当前文件找到其声明,编译器就认为编译正确。而寻找使用变量定义的这项任务就被留给了链接器。链接器的其中一项任务就是要确定所使用的变量要有其唯一的定义。但为了让链接器工作的轻松一点编译器还是多做了一点工作的,这部分工作就是 符号表(Symbol table)。

符号表(Symbol table)

  编译器在编译过程中每次遇到一个全局变量或者函数名都会在符号表中添加一项,最终编译器会统计一张符号表。

static用法:如果你认为一个变量只应该被当前文件使用而不暴露给外部,那么你就可以使用static关键字修饰一下。

本质上整个符号表只是想表达两件事:

  • 我能提供给其它文件使用的符号
  • 我需要其它文件提供给我使用的符号

符号表存放在哪里

obj file

静态链接下可执行文件的生成

objToExe

  可执行文件区别于目标文件的地方在于,可执行文件有一个入口函数,这个函数也就是我们在C语言当中定义的main函数,main函数在执行过程中会用到所有可执行文件当中的代码和数据。main函数被操作系统调用执行。

  你可以把可执行文件生成的过程想象成装订一本书,一本书中通常有好多章节,这些章节是你自己写的,且一本书不可避免的要引用其它著作。静态链接这个过程就好比不但要装订你自己写的文章,而且也把你引用的其它人的著作也直接装订进了你的书里。这些工作完成后,只需要按一下订书器,一本书就制作完成啦。
  在这个比喻中,你写的各个章节就好比你写的代码,引用的其它人的著作就好比使用其它人的静态库,装订成一本书就好比可执行文件的生成。

动态库

  将静态链接生成可执行文件的过程比作了装订一本书,静态链接将引用的其它人的著作也装订到了书里,而动态链接可以想象成作者仅仅在引用的地方写了一句话,比如引用了“xxx”,那么作者就在引用的地方写上“此处参考“xxx”,那么读者在读到该处就会自行查找相应内容,其该过程就是动态链接的基本思想了。
  因此我们就可以知道helloworld程序中的printf函数到底是在哪里定义的,答案就是该函数是在libc.so当中定义的,Linux下编译链接生成可执行文件时会默认动态链接libc.so(Windows同理),使用ldd命令可查看可执行文件的依赖项(libc.so)。因此虽然你从没有看到过printf的定义也可以正确的使用这个函数。

动态链接

  动态链接可以在两种情况下被链接使用,分别是load-time dynamic linking(加载时动态链接) 以及 run-time dynamic linking(运行时动态链接)。
  把加载理解为程序从磁盘复制到内存的过程,加载时动态链接就出现在这个过程Windows下比较常见的启动错误问题,就是因为没有找到依赖的动态库,如下图:


can't find dll
加载时动态链接
  • 阶段一,将动态库信息写入可执行文件。在编译链接生成可执行文件时,需要将使用的动态库加入到链接选项当中;
  • 阶段二,加载可执行文件时依据动态库信息进行动态链接。

  为加深对加载时动态链接这个过程的理解,类比一下:沿用前几节读书的例子,我们正在读的书中引用了“xxx”,那么加载时动态链接就好比读者开始准备读这本书的时候(还没有真正的读)就把所有该书当中引用的资料著作都找齐放到一旁准备查看。在这个类比当中,开始读书前的准备工作就好比加载时动态链接。

运行时动态链接

  不需要在编译链接时提供动态库信息,也就是说,在可执行文件被启动运行之前,可执行文件对所依赖的动态库信息一无所知,只有当程序运行到需要调用动态库所提供的代码时才会启动动态链接过程。
  运行时动态链接就好比直接拿起一本书开始看,看到有引用的参考文献时再去找该资料。运行时动态链接更像是我们平时读书时的样子。
PS:在编译链接过程中,可以同时使用动态库以及静态库。这两种库的使用并不冲突,那么在这种情况下生成的可执行文件中,可执行文件中包含了静态库的数据和代码,以及动态库的必要信息。

动态库vs静态库

动态库优点:

  方便了程序升级和bug修复。如果我们修改了动态库的代码,只需重新编译动态库即可,因为可执行文件当中仅仅保留了动态库的必要信息,重新编译动态库后这些必要都信息不会改变(只要不修改动态库的名字和动态库导出的供可执行文件使用的函数),编译好新的动态库后只需要简单的替换原有动态库,下一次运行程序时就可以使用新的动态库了。我们平时使用都客户端程序,比如:QQ,输入法,播放器,都利用了动态库的这一优点,原因就在于方便升级以bug修复,只需要更新相应的动态库就可以了。
  插件的实现。我们知道动态链接可以出现在运行时(run-time dynamic link),动态链接的这种特性可以用于扩展程序能力,那么如何扩展呢?你肯定听说过一样神器,没错,就是插件。你有没有想过插件是怎么实现的?实现插件时,我们只需要实现几个规定好的几个函数,我们的插件就可以运行了,可这是怎么做到的呢,答案就在于运行时动态链接,可以将插件以动态的都方式实现。我们知道使用运行时动态链接无需在编译链接期间告诉链接器所使用的动态库信息,可执行文件对此一无所知,只有当运行时才知道使用什么动态库,以及使用了动态库中哪些函数,但是在编译链接可执行文件时又怎么知道插件中定义了哪些函数呢,因此所有的插件实现函数必须都有一个统一的格式,程序在运行时需要加载所有插件(动态库),然后调用所有插件的入口函数(统一的格式),这样我们写的插件就可以被执行起来了。
  多语言编程。我们知道使用Python可以快速进行开发,但Python的性能无法同C/C++相比(因为Python是解释型语言),有没有办法可以兼具Python的快速开发能力以及C/C++的高性能呢,答案是可以的,我们可以将C/C++代码编译链接成动态库,这样python就可以直接调用动态库中的函数了。不但Python,Perl以及Java等都可以通过动态库的形式调用C/C++代码。动态库的使用使得同一个项目不同语言混合编程成为可能,而且动态库的使用更大限度的实现了代码复用。

动态库缺点:

  动态库中的代码是地址无关代码(Position-Idependent Code,PIC,因此在使用动态库中的代码时程序要多做一些工作。
  动态链接下的可执行文件不可以被独立运行(这里讨论的是加载时动态链接,load-time dynamic link),换句话说就是,如果没有提供所依赖的动态库或者所提供的动态库版本和可执行文件所依赖的不兼容,程序是无法启动的。动态库的依赖问题会给程序的安装部署带来麻烦。

静态库优点:

  静态链接下的可执行文件由于不依赖任何库,因为部署非常方便,仅仅用一个新的可执行文件进行覆盖就可以了,因此极大的简化了系统部署以及升级。

静态库缺点:

  会导致可执行文件过大,且多个程序静态链接同一个静态库的话会导致磁盘浪费的问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容