2022年程序员的自我修养静态链接 .pdf
《2022年程序员的自我修养静态链接 .pdf》由会员分享,可在线阅读,更多相关《2022年程序员的自我修养静态链接 .pdf(20页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、程序员的自我修养3静态链接Table of Contents 1、链接最实际的做法-相似段合并 .1 2、链接的两个步骤.3 2.1,空间与地址分配.3 2.2,符号解析与重定位。.6 2.2.1 查看反汇编代码(objdump-d),效果如下,.6 2.2.2 那链接器是怎么知道怎么调整?.7 2.2.3 符号地址修改的过程.8 3、解决一个疑问.9 4,API与库.9 5、链接过程控制.14 5.1 控制方法.14 5.2 最小程序 内嵌汇编、系统调用、自建链接脚本.15 5.3 ld 链接脚本语法简介.18 6、用到的一些命令:.20 1、链接最实际的做法-相似段合并有了目标文件之后,接
2、下来的问题就是如何将它们组合起来,形成一个可以使用的程序或者更大的模块,这就是静态链接要解决的问题,静态链接是链接的核心内容。现在有两个文件:a.c b.c 名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 20 页 -程序员的自我修养3静态链接编译成目标文件并查看符号表:名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 20 页 -程序员的自我修养3静态链接可以看出,b.c 总共定义了两个全局符号,变量shared和函数 swap,a.c中只有一个全局符号main。模块 a.c 引用了 b.c 里的两个全局变量。我们接下来要做的就是链接这两个文件,最终形成可执行文件a
3、b.我们知道可执行文件中的代码段和数据段都是由输入的目标文件中合并而来的。这样就产生了第一个问题,对于多个输入文件,链接器怎样将他们的各段合并到输出文件?最简单的方案:简单的叠加,即A 有 5 个段,B 有 5 个段,C 有 5 个段,那么可执行文件中将有15 个段。这种做法非常浪费空间,因为对于X86 硬件来说,段的装载地址和空间对齐的单位是页,也就是4K,也就是说如果段只有1 字节,也要占4K 空间,这样会造成内存空间大量的内部碎片,所以这并不是好的方案。最实际的做法:相似段合并,其中.bss 段在目标文件和可执行文件中并不占用空间,但是在装载的时候占用地址空间。所以链接器合并各段的时候也
4、要.bss 合并,并且分配虚拟空间。2、链接的两个步骤空间与地址分配。扫描所有的输入文件,获得它们的各个段的长度、属性和位置。将输入文件中的符号表中所有的符号定义和符号引用收集起来,统一放到一个全局符号表。合并相应段,计算出合并后的段的长度和位置,并建立映射关系。符号解析与定位。这是链接的核心,根据输入文件中的段信息、重定位信息,进行符号解析和重定位、调整代码中的地址。2.1,空间与地址分配执行链接操作:名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 20 页 -程序员的自我修养3静态链接a.o 和 b.o 的段情况如下:名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,
5、共 20 页 -程序员的自我修养3静态链接为输出文件分配地址和空间有两种含义,一种是输出的可执行文件的空间;第二个是在装载后的虚拟地址中的虚拟地址空间。对于.text 和.data 这样的实际地址中的段在文件和虚拟地址中都要分配空间,但是.bss 这样的段,分配空间的意义只限于虚拟地址空间,因为它在文件中没有内容。以后谈空间分配,就是虚拟空间分配,因为这个关系到链接器后面关于地址的计算步骤,而可执行文件本身的空间分配与链接的过程关系不大。VMA 虚拟地址LMA 加载地址正常情况下,两个值应该是一样的,但是在有些嵌入式系统中可能不同。我们只关注VMA。在链接之前,目标文件中所有段的VMA 都是
6、0,因为虚拟空间还没有被分配。链接后程序中使用的地址已经是虚拟地址,我们关心VMA 和 size 忽略文件偏移。必须使用虚拟地址,因为每次装载后在内存中的地址才是真实的物理地址,而且由于分页映射机制,物理地址在运行前不可能被确定!只能是分段机制进行地址隔离,虚拟映射,搞的好像每个程序都占用3GB 的可用内存!编程时使用的或者链接后符号地址、在文件中的偏移地址都不可用,实际地址和虚拟地址的映射由操作系统和MMU负责。名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页,共 20 页 -程序员的自我修养3静态链接上图忽略了像.comment这种无关紧要的段,本例又没有.bss 段,所以只关心代
7、码段和数据段。为什么链接器要将ab 的代码段分配到ox08048094,数据段分配到ox08049108,而不是从虚拟地址的 0 地址开始分配呢?这涉及操作系统的进程虚拟地址空间分配规则,在 linux下,ELF可执行文件默认从地址0 x08048000开始分配。段的虚拟地址确定以后,链接器要开始计算各个符号的虚拟地址,因为各个符号在段内位置固定,ld 要为每个符号计算一个偏移量。这个很好计算,三个全局符号的地址被计算出来后,全局符号表被更新。2.2,符号解析与重定位。因为程序内部要使用虚拟地址,所以要对代码进行调整。2.2.1 查看反汇编代码(objdump-d),效果如下,对于 share
8、d 变量引用:Link 之前,在 a.o 中引用shared变量链接之后,查看符号表:名师资料总结-精品资料欢迎下载-名师精心整理-第 6 页,共 20 页 -程序员的自我修养3静态链接发现 shared 的虚拟地址是080490f8。与是查看 ab 的反汇编代码中对shared的引用:而 a.o 对swap的调用:(这是一条近址相对位移调用指令,调用下一条指令,这只是临时的假调用)在 ab 中对 swap 的调用:可以看出在目标文件中,对shared和 swap 的引用都是用假地址来代替,真正的地址计算工作留给了链接器,而在第一步的地址和空间分配后,已经可以确定所有的符号的虚拟地址了。那么链
9、接器可以对每个需要重定位的指令进行地址修正。2.2.2 那链接器是怎么知道怎么调整?哪些指令是要被调整的呢?这些指令的哪些部分需要调整?怎么调整?ELF文件中有个叫重定位表的结构专门来保存这些与重定位相关的信息。名师资料总结-精品资料欢迎下载-名师精心整理-第 7 页,共 20 页 -程序员的自我修养3静态链接对于可重定位文件来说,它必须包含重定位表。重定位表是一个结构体数组,每个结构体描述一个重定位入口,使用objdump r 参数查看重定位表:我们看到重定位表的内容,意思是在.text 段的 15 偏移量的地方有一个叫shared 的变量地址需要重定位,我们查看代码段的命令偏移量 15 处
10、正是 shared的假地址。Swap 同理。每一个要被重定义的地方都叫一个重定位入口。2.2.3 符号地址修改的过程应该是,查看 a.o 的符号表 发现有两项GLOBAL 类型的所在段属性是UND 即未定义,即这两项是需要重定位的,然后在链接的第一步时生成的全局符号表 中找这两个项,找不到则报错,找到了则根据 重定位表 中的偏移找到要修改的重定位点。具体的过程,因为指令的寻址方式有很多种,所以并不是直接从全局符号表中拿出真实的地址就可以用的!在重定位表中有一项TYPE:R_386_32 绝对寻址修正 S+A R_386_PC32 相对寻址修正S+A-P A 代表被修正位置的值,S 表示符号的实
11、际地址,P 代表被修正的位置。两种方式的区别在于绝对寻址修正后的地址为该符号的实际地址,相对寻址修正后的地址为被修改指令距符号的距离差。名师资料总结-精品资料欢迎下载-名师精心整理-第 8 页,共 20 页 -程序员的自我修养3静态链接3、解决一个疑问在上个文档中我们发现,本该和未初始化的静态局部变量一起放在.bss 段的未初始化全局变量,并没有出现在.bss 段,而是被标记为COMMON类型的变量,原因是它是一个弱符号,大小并不确定,可能在多个目标文件中都有定义,在链接的过程中可能被强符号取代,也可能被其它比较大的弱符号取代,所以只有在链接后才能最终确定大小,最终还是会在.BSS段分配空间。
12、所以从总体来看,未初始化的全局变量还是被放在BSS段的。4,API 与库运行库(Run-time Library)是程序运行时需要的支撑库。对于 C/C+程序来说,其运行库一定是动态链接库或者共享库,而不是静态库。比如,很多程序的运行需要 C 标准库以及其它一些库的支持,那么 C 标准库或者需要的库就是此程序的运行库。如果在程序连接的时候对所有的库进行的是静态连接,那么程序运行的时候就不需要任何运行库的支持。操作系统 API提供在这个操作系统上与任何东西互操作的能力:文件、内存、时钟、网络、图形、各种外设等等。API 通常还提供许多工具类的功能:操纵字符串、各种数据类型、时间日期等等。每个 A
13、PI 函数的实现可能由一个系统调用实现,也可以由多个系统调用实现,当然,也可以不使用系统调用,系统调用是内核留给用户的接口,以C 库 API 的方式进行封装。而一些语言库就是对操作系统API 的封装。大致层次:应用程序-语言库-C 运行库(API 提供者)-系统调用内核服务。在LINUX中 C 语言标准库是个例外,它本身就是提供API 的 C 运行库的一部分。比如printf函数在 linux下是一个 write系统调用,而在window下则是一个writeconsole系统 API。世界上最通用的操作系统 API 其实是传统Unix 的 POSIX 接口(可移植操作系统接口),标准 C 的标
14、准库其实就是这个接口的子集,所有类 Unix 操作系统所提供的操作系统 API,几乎都被称为 libc(对 C 库的传统称呼),所有操作系统所提供的自然操作系统接口都是以 C 语言执行库的方式提供的。Windows 操作系统上提供的 Windows API 与POSIX 不同,但也是 C 函数库。因为无论是windows还是 Linux 系统 API 都是 C 语言接口,而且一般以动态库(运行库)的方式提供,所以又称为C 运行库。它们通过或不通过系统调用实现保罗万象的功能。而且一般包含名师资料总结-精品资料欢迎下载-名师精心整理-第 9 页,共 20 页 -程序员的自我修养3静态链接C 标准库
15、的静态库和动态库(C 语言标准运行库),是C 标准库的超集,也就是说glibc和MSVCRT都对其进行了丰富的扩展。比如像线程操作这样的函数并不是C 标准库的一部分。glibc提供一组头文件和一组库文件,最基本、最常用的 C 标准库函数和系统函数在libc.so 库文件中,几乎所有C 程序的运行都依赖于libc.so,有些做数学计算的C 程序依赖于libm.so,以后我们还会看到多线程的C 程序依赖于libpthread.so。以后我说libc 时专指libc.so 这个库文件,而说glibc 时指的是 glibc 提供的所有库文件上图依然正确:第一层,应用程序调用API,展开了说就是,应用程
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2022年程序员的自我修养静态链接 2022 程序员 自我 修养 静态 链接
限制150内