第4章 静态链接

静态链接是指将目标文件链接在一起形成可执行文件的过程。

4.1空间与地址分配

4.1.2相似段合并

静态链接过程是把各目标文件中的各段合并到可执行文件中的相应段中。

链接器为目标文件分配地址和空间。这个空间有两层含义,既包括在可执行文件中占有的空间也包括在虚拟地址中分配的空间。其中虚拟地址空间的分配关系重大。

静态链接的过程一般分两步——1、空间与地址分配。2、符号解析与重定位。

第一步就是获取段信息,合并段将它们映射到可执行文件的段表信息中。整理符号和引用并放入全局符号表中。

第二步,实际上就是链接,把目标文件中的地址呀、符号呀、数据等进行重定位然后链接。

VMA:Virtual Memory Address

LMA:Load Memory Address

链接前的VMA都是0,链接后就有实实在在的地址了。

4.1.3符号地址的确定

符号地址在原来的目标文件中的每个段中都有一个偏移量,这个偏移量是固定的,所以在链接的过程中只要在虚拟地址的基础上再加上这个偏移量就是某符号在虚拟地址空间中的地址。

4.2符号解析与重定位

在空间和地址分配完成以后,链接器即将进行符号解析与重定位。本小节举了个例子,用了很多汇编代码,有些晦涩难懂。

目标文件中使用的都是虚拟地址不是物理地址,这一点很重要。目标文件的起始地址都是0。

4.2.2重定位表

它存储着与重定位相关的信息。

每个要被重定位的ELF段都对应一个重定位表,重定位表本身也是一个段,所以你也可以叫重定位表为重定位段。

每一个要被重定位的地方叫做重定位入口。

重定位入口的偏移表示入口在要被重定位的段中的位置。

重定位表的实质是一个Elf32_Rel的结构体数组,每个数组元素对应一个重定位入口。

4.2.3符号解析

重定位的过程伴随着符号解析的过程。

每个重定位的入口对应一个符号引用,链接器会查找有所有目标文件的符号表所组成的全局符号表,然后根据这个全局符号表进行重定位。

4.2.4指令修正方式

32位x86平台下的ELF文件的重定位入口所修正的指令寻址方式只有2种:绝对近址32位寻址和相对近址32位寻址。

修正的位置长度为4bytes。

经过绝对地址修正方式修正得到的地址是该符号的实际地址,而相对地址寻址方式得到的是符号与被修正位置的距离。

4.3

COMMON块

相同的符号定义在多个不同的目标文件中,但是类型各不相同,这说明它们不是同一个变量或者函数,因此不能对它们进行相同的操作。但是链接器只认符号不认类型,它认为它们都一样。

这种情况主要分为3种:

1、至少2个强符号类型不一致。

2、一个强符号和多个弱符号类型不一致。

3、至少2个弱符号类型不一致。

强符号是指定义在目标文件中全局性符号,包括函数和变量,显然它们如果有相同的多个,那就是重定义,这本身就会报错。

现在的编译器和链接器都支持COMMON块机制。它主要针对的对象是弱符号。如果在众多符号之中有一个符号是强符号,那么符号所占空间与强符号相同。如果弱符号大小超过强符号,编译器会发出警告。

编译器为什么不把未初始化的全局变量当做未初始化的局部静态变量处理?为什么不在bss中给它们分配空间,而非要把它们标记为COMMON类型呢?

因为编译时编译器不知道弱符号需要多大空间,所以这时无法为其在BSS中分配空间,只能当做局部静态变量处理。但是在链接的时候可以确定,所以链接以后才在BSS中分配空间。

编译器把所有未初始化的全局变量都当成COMMON类型处理,这样做是为了与强类型分开,凡是非COMMON类型的都是强类型。多个强类型的符号会发生重复定义的错误。

4.4.1重复代码消除

C++在很多时候会产生重复代码,模版是其中最具代表性的一个。模版可以在不同的编译单元被实例化成相同的类型,两个完全一样的类是完全没有必要的,一个足矣。

不解决代码重复问题会导致:

1、空间浪费。这个根本就不用解释。

2、地址容易出错。因为是多个相同的实例嘛,就会有多个指针分别指向这些实例,但是这些实例之间没差别,它们在逻辑上是同一函数,这就容易造成指针的误指。

3、指令运行效率较低。缓存机制会缓存多份重复的代码,但是程序只会用特定的一份,在这么多份相同的代码中找特定的一份不好找,成功率较低,即,缓存命中率低。

解决方案:把每个编译单元中的每个模版的不同实例分别放进不同的段中,并且对不同的单元都这样做,这样在最后链接的时候不同编译单元中的相同实例段就合并从而消除多份相同的实例。

缺点:不同的编译单元可能使用了不同的编译器版本或者优化选项,这会导致实际产生的代码不同,链接器必须选择其中一个副本。

函数级别链接:默认情况下链接器会把所有的目标文件链接在一起,不管有用的代码还是没用的代码,这会导致可执行文件很大。

所谓函数级别链接就是每个编译单元也把函数单独放进一个段中,在链接的时候只链接那些有用的函数段。

这种做法会减慢编译和链接的过程,因为段的数量增加了。

4.4.2全局构造与析构

在C++中全局对象的构造在main之前完成,析构在main之后完成。

在ELF文件中有.init和.fini两个段。

init段包含了进程的初始化代码,在main之前执行。

fini段包含了进程的终止代码,在main之后执行。

C++的全局构造和析构由此实现。

4.4.3

C++与ABI

把不同编译器产生的目标文件链接在一起需要特定的条件——相同的ABI(Application

Binary Interface)。

ABI:符号修饰标准、变量内存布局、函数调用方式等与二进制兼容性相关的内容。

C语言间的目标文件能否互相兼容具体决定于如下几个方面:

1、内置类型大小和存储方式。

2、组合类型大小和存储方式。

3、外部符号与用户定义的符号之间的命名方式和解析方式。

4、函数调用方式。

5、堆栈分布方式。

6、寄存器使用方式。

C++在这方面的决定因素P141+P142介绍。

C++代码不仅对于由不同编译器编译得到的目标文件不兼容,而且就算是同一编译器的不同版本编译得到的目标文件也不兼容。

这都是ABI闹的。

4.5静态库链接

开发环境往往附带语言库,这些库是对系统API的封装。大部分的C语言库函数都调用了系统API,少数除外。

静态库实际上可以看成是一组目标文件的集合。

C语言中看似简单的库函数和系统中众多的API存在着依赖关系。

静态链接的过程分为三步:

1、调用C语言编译器。把C语言程序变成汇编语言程序。

2、调用汇编器。把汇编程序变成目标文件。

3、调用链接器链接成可执行文件。

4.6链接过程控制

WINDOWS内核其实就是一个文件——\WINDOWS\system32\ntoskrnl.exe。

虽然大多数情况下,链接器是对目标文件进行链接,但是对某些系统软件来说却不是这样的。

4.6.1链接过程脚本

有几种控制链接过程的方法:

1、使用命令行来指定参数。这就像WINDOWS下的CMD和LINUX下的SHELL。

2、编译器通过把链接指令存储进目标文件中实现自动链接。

3、用链接脚本来控制连接过程。

4.6.2最“小”的程序

C语言程序的执行入口其实不是main而是库中的_start。

编译器会把只打印字符串的printf优化为puts。

4.6.3使用ld链接脚本

链接控制脚本是用一种特别的语言写成包含为数不多操作。

输入文件中的段是输入段,输出文件中的段是输出段,链接控制的过程就是从输入段到输出端的过程。

4.6.4

ld链接脚本语法简介

它由命令语句和赋值语句组成。

总体来讲它类似于C语言。

本节就是大概地介绍了一下链接脚本语法规则而已。

4.7

BFD库

BFD(Binary File Descriptor Library)为不同目标文件格式提供统一接口,达到跨平台。

你需要做的只是操作抽象的目标文件。

现在编译器通常不直接处理目标文件,而是通过BFD。

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

推荐阅读更多精彩内容