程序员的自我修养(二)

共享对象在编译时不能假设自己在进程虚拟地址空间中的位置,可执行文件可以确认。

装载时重定位

如果使用静态链接的重定位方法,即模块装载地址确认,对所有绝对地址引用重定位。但是指令部分是多个进程共享的,装载时重定位需要修改指令,所以指令部分不能这么做。而可修改数据部分对于不同进程有多个副本可以这么做。

地址无关代码-fPIC

只用装载时重定位无法做到指令部分在多个进程之间共享。引入地址无关代码,就是把代码中可修改部分提取出来跟数据放在一起,在每个进程中拥有一个副本。因此引入.got(全局偏移表)。程序需要访问变量时,先找到got然后根据变量中对应的项找到变量的目标地址。

PLT延迟绑定

使用动态链接需要间接寻址,比较慢。所以使用PLT在函数第一次被用到时才进行绑定。每个外部函数在PLT中有个对应的项, bar@plt:

bar@plt:
jmp *(bar@GOT) //got中保存的项
push n
push moduleID
jump _dl_runtime_resolve

如果初始化过,那么第一条指令就是跳转地址。如果不是,第一条指令会跳转到第二条指令。

.rel.plt

n为符号引用在该重定位表下的下标。ELF将GOT拆成两个表.got|.got.plt .got保存数据(全局变量),.got.plt保存函数引用被剥离出来。

。got.plt

第一项保存.dynamic
第二项保存本模块ID
第三项保存_dl_runtime_resolve地址

PLT本身为地址无关代码,可以和代码段直接合并。

可以不用PIC,那相当于指令部分不能共享浪费内存。

.dynamic段

.dynamic段保存了动态链接器所需要的基本信息 例如依赖哪些共享对象 动态链接符号表的位置 动态链接重定位表的位置 共享对象初始化代码的地址 .dynamic其实就是动态链接的elf文件头

.dynsym为动态符号表,保存与动态链接相关的符号.symtab包括全部符号

PIC代码还需要重定位吗,需要

代码不需要了,因为剥离出来到了GOT。除此之外还有绝对地址引用的数据部分也需要重定位。.rel.dyn.got及数据段,是数据部分的重定位.rel.plt是函数部分的修正

装载共享对象

.dynamic段中有一种类型入口是DT_NEED即依赖的共享对象。加载进来后,遍历重定位表,将got/plt需要重定位的位置进行修正。

你可能感兴趣的:(程序员的自我修养(二))