719: e9 e2 fe ff ff jmp 600
变成一般的地址是这样的
|
这里旁边的 719 就是这个 ELF 文档和起始地址相比的偏移量,而在里面的 e9 e2 fe ff ff 假如写成看的往后退 0x11e 因为这是 ff ff fe e2(intel 是 little endian 表示方法)所表示的 -0x11e 的数。假如把 719 加上 5 再减去 600 就是这个数了。这便是处理器的相对转移。
更有另一种转移方式,就是绝对转移。
|
这个假如用最简单的代码来表示是
|
很明显,就是把 eip 的内容变成了eax 中的内容,假如用 jmp 也是相同的
|
上面的两种转移方式适应于不同的环境需要,假如是在一个ELF文档中的,采用相对转移可带来的好处有以下的几点:
1、能够不用再访问一次内存,在指令的执行时间上得到了大大的提高(这在PCI的总线结构中现在主流的最高主频是133MHZ,而随便一个INTEL CPU的主频都能超过他)。
2、能够适应在动态加载和动态定位的内存环境,而不用再对原来的代码修改便能实现(代码段也不能在运行的时候修改),因为整个动态链接库或可执行文档都是以连续的地址映射的。
但同样带来了几个问题:
1、这样的相对转移没有办法在运行的时候准确的转移到别的动态链接库中的函数地址(因为虽然大部分的动态链接库的加载地址是能够预计的,但从理论上来说是随机的)。
2、这样的代码在平台之间的移植性带来很大的问题,因为不同的机器没有办法知道这样的数字是代表一个地址,还是代表了一个二进制数。所以在对平台移植有高需要的体系中用的是c 的虚函数指针------相对地址转移的发展。如COM,corba体系中就是这样的。
上面的这两项缺点正好是绝对转移的优势。作一个对比,绝对转移就相当于内存寻址时的立即寻址,而相对转移相当于内存寻址的相对寻址。
在一般的动态链接库中实际运用更是用了一个聪明的办法。请看下一段的汇编语言片段:
|
这里的2f7中的call 2fc
那后面的add x10b0,離又是什么用处?假如我们这里假定在内存中的地址是2fc,那加上10b0之后的值是0x13ac了,看在这里是什么呢?
|
这是个got节, 他的全称是global object table 就是全局对象表。他这里存储着要转移的地址。假如在动态链接库中,或是要调用一个在他之外的函数是怎样实现呢?我们往下看:
|
这里就要调用一个call 2e0
|
很明显,我们前面已说了離中所保存的就是.got节的起始地址,而这里就是转移到在.got起始地址偏移0xc处所存储的地址量。而0x2e0所在的地址是在.plt(procedure linkage table)的节中。正是plt got的互相配合,才达到了动态链接的效果。下面的_dl_relocate_object函数就是在把动态链接库加载之后将got中的内容初始化的作用,作好了以后函数解析的准备。
三、_dl_relocate_object函数分析
举个例子。同样来自上面的动态链接库文档中内容。假如我们在这里面调用了printf这个普通的函数,他的rel在文档中的位置是




