ELF静态链接
一直对ELF目标文件是怎样链接成可执行文件感到比较的疑惑,ELF文件里面的重定位段是怎样解决符号引用问题的?前几天偶然看了《深入理解计算机系统》里面讲了这个问题,看了之后对里面的实现机制终于有了一定的理解。
当有链接器链接多个可重定位的共享对象时,共享对象时怎样合并的呢?很简单,链接器将相同类型的节合并在一起,比如将所有输入文件的.text合并到输出文件的.text段,接着是.data段,.bss段等。
链接器扫描所有的输入目标文件,并且获得它们各个节的长度,属性和位置,并将输入目标文件中的符号表中所有的符号定义和符号引用收集起来,统一放到一个全局符号表中。链接器能够获得所有输入目标文件的节长度,并将它们合并,并计算出输出文件中各个节合并后的段长度和位置,并建立段和节之间的映射关系。也就是说,在将输入文件的各个节映射到段中后,输入文件中的各个节在链接后的虚拟地址就已经确定了,那么全局符号表中的符号地址就可以知道了。
知道了定义符号的虚拟地址,合并了各个节,是不是就大功告成了?显然没有这么简单,由于输入给链接器的文件都是可重定位的目标文件,这些目标文件中引用符号的地方存放的地址肯定不是最终的虚拟地址,因为这个时候符号地址还不确定。那么链接器在知道了符号的各个虚拟地址后怎样来修改引用符号的地址为实际的符号虚拟地址呢?这个工作是通过重定位目标文件中的重定位表来实现的。
对于每个要重定位的ELF节都有一个对应的重定位表,而一个重定位表往往就是ELF文件的一个节。比如代码节.text有要重定位的地方,那么会有一个相对应的.rel.text的节保存.text的重定位表;如果数据节.data有要被重定位的地方,也会有一个相对应的叫.rel.data的节与之对应。
每个要被重定位的地方叫做一个重定位入口(Relocation Entry),重定位入口的偏移(Offset)表示该入口在要被重定位的节中的位置。重定位表的结构很简单,它是一个Elf32_Rel结构的数组,每个数组元素对应一个重定位入口。Elf32_Rel定义如下:
typedef struct elf32_rel {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
r_offset表示重定位入口的偏移。对于可重定位文件来说,这个值是该重定位入口所要修正的位置的第一个字节相对于节起始的偏移;对于可执行文件或共享对象文件来说,这个值是该重定位入口所要修正的位置的第一个字节的虚拟地址。
r_info表示重定位入口的类型和符号。这个成员的高8位表示重定位入口的类型,低24位表示重定位入口的符号在符号表中的下标。
- R_386_PC32:重定位一个使用32位pc相对地址的引用。
- R_386_32:重定位使用32位绝对地址的引用。
链接器中根据重定位表来修改符号引用地址的算法是:
第一行迭代每个节,第二行迭代这个节中的每一个重定位表项,为了简单,我们假设节是一个字节数组,每个重定位表项都是Elf32_Rel类型,并假设当这个算法运行时,链接器也已经知道了每个节的运行时地址(定义为ADDR(s))和每个符号的运行时地址(定义为ADDR(r.symbol))。
根据重定位入口的类型,需要分两个情况来修改引用地址。
重定位PC相对引用
假设函数printf在main函数被调用,现在来看下它在main函数中是怎么被调用的:
1b: e8 fc ff ff ff call <main+0x7>
我们可以看到call指令在节的偏移量为0x7,这条指令由1个字节的操作码0xe8和32比特的引用0xfffffffc(-4)组成。我们也可以看到对这个引用的重定位项:
r.offset = 0x7
r.symbol = printf
r.type = R_386_PC32
重定位的项告诉链接器修改32位PC相对引用在节的偏移量为0x7,现在假设链接器已经决定了:
ADDR(s) = ADDR(.text) = 0x80483b4
ADDR(r.symbol) = ADDR(printf) = 0x80483c8
使用前面的算法来计算引用的运行时地址:
refaddr = ADDR(s) + r.offset
= 0x80483b4 + 0x7
= 0x80483bb
修改引用符号地址的值,使得引用指向printf运行时的地址:
*refptr = (unsigned)(ADDR(r.symbol) + *refptr - refaddr)
= (unsigned)(0x80483c8 + (-) - 0x80483bb)
= (unsigned)(0x9)
这样,printf的重定位值就为0x9。当程序运行时,由于call指令时相对pc寻址的,所以计算出的printf的实际地址为:
pc + 0x9 = 0x80483bf + 0x9 = 0x80483c8
重定位绝对地址引用
假设定义了下面一个全局变量:
int *bufp0 = &buf[]
由于bufp0是一个初始化了的数据对象,它会被存放在.data节中。由于它被初始化为全局变量buf[0]的地址,因此bufp0需要被重定位。下面是bufp0在.data节中的反汇编代码:
<bufp0>
:
我们可以看到.data节中包含了一个简单的32位的引用,指针bufp0的值位0x0。重定位表项告诉链接器这是一个32位的绝对地址引用,相对于.data节的偏移量为0。它必须被重定位以指向buf[0]的地址。假设链接器已经决定了:
ADDR(r.symbol) = ADDR(buf) = 0x8049454
链接器将根据上面的算法更新这个引用:
*refptr = (unsigned)(ADDR(r.symbol) + *refptr)
= (unsigned)0x8049454 + )
= (unsigned)(0x8049454)
ELF静态链接的更多相关文章
- 实例分析ELF文件静态链接
参考文献: <ELF V1.2> <程序员的自我修养---链接.装载与库>第4章 静态链接 开发平台: [thm@tanghuimin static_link]$ uname ...
- ELF动态链接
为什么要使用动态链接? 在现代的linux系统中,假设一个普通的程序会使用到c语言静态库至少1MB以上,那么,如果我们的机器运行100个这样的程序,就用浪费近100MB的内存:如果磁盘有2000个这样 ...
- C/C++ 静态链接库(.a) 与 动态链接库(.so)
平时我们写程序都必须 include 很多头文件,因为可以避免重复造轮子,软件大厦可不是单靠一个人就能完成的.但是你是否知道引用的那些头文件中的函数是怎么被执行的呢?这就要牵扯到链接库了! 库有两种, ...
- [CSAPP-II] 链接[符号解析和重定位] 静态链接 动态链接 动态链接接口
1 平台 转http://blog.csdn.net/misskissc/article/details/43063419 1.1 硬件 Table 1. 硬件(lscpu) Architecture ...
- 关于C语言静态链接的个人理解,欢迎指正
摘要:本篇主要介绍在静态链接中多个文件合并.地址确定.符号解析和重定位相关问题,以GCC编译器为例. 首先,链接器链接多个文件时,采用何种方式合并为一个文件?方式一,按序叠加,即多个文件依次叠 ...
- GCC编译器原理(三)------编译原理三:编译过程(3)---编译之汇编以及静态链接【2】
4.1.2 符号解析与重定位 (1)重定位 在完成空间和地址的分配步骤之后,链接器就进入了符号解析和重定位的步骤,这是静态链接的核心部分. 先看看 a.o 的反汇编文件: objdump -d a.o ...
- GCC编译过程与动态链接库和静态链接库
1. 库的介绍 库是写好的现有的,成熟的,可以复用的代码.现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常. 本质上来说库是一种可执行代码的二进制形式,可 ...
- Linux 静态链接库和动态连接库
(0)文件夹 VMware 下安装Ubuntu的吐血经历 零基础学习Shell编程 Linux下的makefile的妙用 Linux调试神器 -- gdb 十分钟学会Python的基本类型 Linux ...
- 怎么快速构建自己的C/C++程序?——有关编译、静态链接和SCons
怎么快速构建自己的C/C++程序?--有关编译.静态链接和SCons 1. 写在前面 最初写C++是在Visual Studio这个IDE里,那时我并没有makefile的概念,对程序的编译和链接的一 ...
随机推荐
- SpringMVC自定义处理器里的那些事
一.如何让一个普通类成为Controller? ①:实现接口Controller 解析:handleRequest(request,response) ②:继承AbstractController 解 ...
- webform 光棒效果,删除操作弹出确定取消窗口
鼠标移入onmouseover和鼠标移出onmouseout,代码里没大写我这也就不大写了.那首先,我们得获取Class为tr_item里的所有东西,也就是项标签里的数据.然后呢,我们定义一个oldC ...
- bzoj1510: [POI2006]Kra-The Disks(单调栈)
这道题可以O(n)解决,用二分还更慢一点 维护一个单调栈,模拟掉盘子的过程就行了 #include<stdio.h> #include<string.h> #include&l ...
- Session 、Application 和 HttpContext 的使用区别
在ASP.NET WEB页面开发中,经常会需要保存一些信息,以便在不同的页面或时间段都能够访问到.这其中便会用到Session和Application. Session .Application 和 ...
- VS加入全局缓存
1. 加载 gacutil.exe /i <DLL Name>.dll 2. 卸载 gacutil.exe /u <DLL Name>
- 5.Powershell变量
在指令执行过程中,会有一些数据产生,这些数据被用于以后的语句,需要一个存储单元暂时的存放这些数据,这个时候定义一个变量来存储数据.例如$string = “Hello Powershell!” Pow ...
- C#_技巧:真伪随机数
使用 Random 产生随机数.(这是一种伪随机数,需要seed,同一个seed后,采用某种算法产生的数字序列都是一样的) 两种写法 错误 for(int i=0;i<100;i++) { ...
- 一起来测试天兔Lepus3.8 Beta版本的MSSQL部分
一起来测试天兔Lepus3.8 Beta版本的MSSQL部分 产品介绍:http://www.lepus.cc/下载地址:http://www.lepus.cc/soft/18手册地址:http:// ...
- SQL Server最近怎样了
SQL Server最近怎样了 又到年终了,大家都作最后冲刺 最近园子里真的多了很多口水帖,无论大家争论得多么激烈,时间依然滴答滴答地过,争论完之后我们依然要继续埋头苦干 为年终奖.为明年做准备 这里 ...
- CYQ.Data V5 分布式缓存MemCached应用开发介绍
前言 今天大伙还在热议关于.NET Core的东西,我只想说一句:在.NET 跨平台叫了这么多年间,其实人们期待的是一个知名的跨平台案例,而不是一堆能跨平台的消息. 好,回头说说框架: 在框架完成数据 ...