![](https://csdnimg.cn/release/download_crawler_static/86391008/bg4.jpg)
这个如果用最简单的代码来表示是
addl $2,%eip
pushl %eip
movl (%eax),%eip
很明显,就是把 eip 的内容变成了 eax 中的内容,如果用 jmp 也是一样的
上面的两种转移方式适应于不同的环境要求,如果是在一个 ELF 文件中的,采用相对转移可带来的好处有
以下的几点:
1、 可以不用再访问一次内存,在指令的执行时间上得到了大大的提高(这在 PCI 的总线结构中现在主流
的最高主频是 133MHZ,而随便一个 INTEL CPU 的主频都能超过它)。
2、 可以适应在动态加载与动态定位的内存环境,而不用再对原来的代码修改便能实现(代码段也不能在
运行的时候修改),因为整个动态链接库或可执行文件都是以连续的地址映射的。
但同样带来了几个问题:
1、 这样的相对转移没有办法在运行的时候准确的转移到别的动态链接库中的函数地址(因为虽然大部分
的动态链接库的加载地址是可以预计的,但从理论上来说是随机的)。
2、 这样的代码在平台之间的移植性带来很大的问题,因为不同的机器没有办法知道这样的数字是代表一
个地址,还是代表了一个二进制数。所以在对平台移植有高要求的体系中用的是 c++的虚函数指针------
相对地址转移的发展。如 COM,corba 体系中就是这样的。
上面的这两项缺点正好是绝对转移的优势。作一个对比,绝对转移就相当于内存寻址时的立即寻址,而相
对转移相当于内存寻址的相对寻址。
在一般的动态链接库中实际运用更是用了一个聪明的办法。请看下一段的汇编语言片段:
2f7: e8 00 00 00 00 call 2fc <ok+0xc>
2fc: 5b pop %ebx
2fd: 81 c3 b0 10 00 00 add $0x10b0,%ebx
这里的 2f7 中的 call 2fc <ok+0xc>是什么意思呢,从我们上面的方法来看,这里是什么呢?就是把函数
运行到了 2fc 处,根据是我上面所说的,因为是一个相对转移。e8 00 00 00 00。如果用一般的观点看这
没有什么用处。但妙处就在这里,2fc 处的 pop %ebx,是把什么送到%ebx 中呢,如果每一次 call 都会
把下一条要执行的指令的地址压入栈中,那%ebx 中在这里的内容就是 2d4 这一条指令在内存中的地址了,
回想动态链接库的绝对地址是没有办法在编译时得到,但这样却可以--------很巧妙,不对吗?
那后面的 add $0x10b0,%ebx 又是什么用处?如果我们这里假定在内存中的地址是 2fc,那加上 10b0
之后的值是 0x13ac 了,看在这里是什么呢?
Disassembly of section .got:
000013ac <.got>:
13ac: 34 13 xor $0x13,%al
...
这是一个 got 节, 它的全称是 global object table 就是全局对象表。它这里存储着要转移的地址。如果
在动态链接库中,或是要调用一个在它之外的函数是怎样实现呢?我们往下看: