The Wikipedia article about x86 assembly表示“程序员无法直接访问IP寄存器。”
直接表示像mov和add这样的指令。
为什么不呢?这背后的原因是什么?有哪些技术限制?
答案 0 :(得分:23)
您无法直接访问它,因为没有合法的用例。任意指令更改eip
都会使分支预测变得非常困难,并可能会引发一系列安全问题。
您可以使用eip
,jmp
或call
修改ret
。您无法使用正常操作直接读取或写入eip
将eip
设置为寄存器就像jmp eax
一样简单。您还可以执行push eax; ret
,它将eax
的值推送到堆栈然后返回(即弹出和跳转)。第三个选项是call eax
,它调用eax中的地址。
阅读可以这样做:
call get_eip
get_eip:
pop eax ; eax now contains the address of this instruction
答案 1 :(得分:8)
这可能是x86的可能设计。 ARM做expose its program counter for read/write as R15。但这很不寻常。
它允许一个非常紧凑的函数序言/尾声,以及使用单个指令推送或弹出多个寄存器的能力:在{= 1}}进入,push {r5, lr}
返回。 (将保存的链接寄存器值弹出到程序计数器中)。
然而,它使得高性能/无序ARM实现不太方便,并且因为AArch64而被删除。
所以可能,但是会使用其中一个寄存器。 32位ARM有16个整数寄存器(包括PC),因此寄存器号需要4位才能在ARM机器代码中进行编码。另一个寄存器几乎总是被绑定为堆栈指针,因此ARM有14个通用整数寄存器。 (LR可以保存到堆栈中,因此它可以用作函数体内的通用寄存器。)
现代x86的大多数都是从8086继承而来的。它采用相当紧凑的可变长度指令编码设计,只有8个寄存器,每个src和dst寄存器只需要3位寄存器。
在最初的8086中,它们不是非常通用的,并且在16位模式下SP相对寻址是不可能的,因此基本上2个寄存器(SP和BP)被捆绑用于堆栈内容。这只留下6个有点通用的寄存器,其中一个是PC而不是通用的,这将大大减少可用的寄存器,大大增加了典型代码中的溢出/重载量。
AMD64增加了r8-r15和RIP相对寻址模式。 pop {r5, pc}
以及用于直接访问静态数据和常量的RIP相对寻址模式,是高效的与位置无关的代码所需要的。间接JMP指令完全足以写入RIP。
通过允许使用任意指令来读取或写入PC,实际上无法获得任何东西,因为您总是可以使用整数寄存器和间接跳转来执行相同的操作。 x86-64的R15几乎与RIP完全相同,特别是对于作为编译器目标的架构性能而言。 (手写的非常奇怪的东西在2000年设计AMD64时已经非常普遍了。)
因此,AMD64真的是第一次有可能获得像ARM这样完全暴露的程序计数器,但有很多很好的理由不这样做。
答案 2 :(得分:3)
我认为它们意味着无法以与访问其他寄存器相同的方式直接访问IP寄存器。程序员肯定可以写入IP,例如通过发出跳转指令。
答案 3 :(得分:3)
jmp
将设置EIP
注册。
此代码将eip设置为00401000:
mov eax, 00401000
jmp eax ;set Eip to 00401000
获取EIP
call GetEIP
.
.
GetEIP:
mov eax, [esp]
ret