ELF中模块间数据引用的重定位

在模块中进行各类数据引用的方式总共分有:模块内数据、函数访问,模块间数据、函数访问,其中模块内的访问在链接时就已经决定了他们的相对偏移,在运行时不再关心这部分的内容,而模块间访问相对就比较复杂了。
为了复用物理内存,发明了PIC/PIE技术,将数据和指令分开GOT和PLT stub,这样重定位代码只需要修改GOT的数据部分就可以访问正确的函数了,ELF的PLT和GOT分析了模块间函数访问过程中的lazy bind机制。

reflink:http://www.qnx.com/developers/docs/qnxcar2/index.jsp?topic=%2Fcom.qnx.doc.neutrino.prog%2Ftopic%2Fdevel_Lazy_binding.html

Lazy binding (also known as lazy linking or on-demand symbol resolution) is the process by which symbol resolution isn't done until a symbol is actually used. 
Functions can be bound on-demand, but data references can't.

函数可以在运行时按需进行动态链接重定位,但是数据访问是不行的,必须在程序启动的时候就完成重定位操作。函数调用可以跳转到plt项,第一次符号引用触发符号解析,后面的函数调用直接就进行跳转,额外的消耗是多jmp了一次。但是数据访问就不同了,数据地址是不可执行的,你只能加载数据而不能跳转到数据地址执行。所以LAZY BIND对于模块间数据引用是不成立的,它总是BIND_NOW。

各种平台上ELF文件有各种各样的section名称:.plt,.got,.plt.got,.got.plt,.rela.plt,不要被名称迷惑,根据section的类型它总共有三种的:

  1. PLT:program independent table,代码段,在运行期间不会改变的,是模块间函数跳转的跳板
  2. GOT:global offset table,数据段,存储有模块间函数调用的地址和模块间数据访问的地址,它可能是分开的section:.got,.got.plt,也可能只有一个.got
  3. relocation:对section进行重定位的表,在符号重定位的时候需要根据重定位项信息进行符号解析和修改GOT中的信息,GOT中不管是数据还是函数的地址重定位都需要引用重定位项信息。

来验证一下我们上面的猜测,在glibc链接器初始化的时候进行了数据符号初始化:

extern char **environ;	//访问libc的数据
int main(int argc, char **argv)
{
	environ++;	//进行计算
	return 0;

打开linker的调试:LD_DEBUG=all ~/tmp/a.out

我们看到它经历了几个步骤:

     26285:	relocation processing: /lib/x86_64-linux-gnu/libc.so.6 (lazy)
     ...
     26285:	relocation processing: /home/linux/tmp/a.out (lazy)   //lazy bind模式
     26285:	symbol=__environ;  lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
     26285:	binding file /home/linux/tmp/a.out [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `__environ' [GLIBC_2.2.5]
     26285:	
     26285:	relocation processing: /lib64/ld-linux-x86-64.so.2
     ...
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页