为什么不能直接设置指令指针?

时间:2011-11-30 21:50:06

标签: x86 cpu-registers program-counter

The Wikipedia article about x86 assembly表示“程序员无法直接访问IP寄存器。”

直接表示像mov和add这样的指令。

为什么不呢?这背后的原因是什么?有哪些技术限制?

4 个答案:

答案 0 :(得分:23)

您无法直接访问它,因为没有合法的用例。任意指令更改eip都会使分支预测变得非常困难,并可能会引发一系列安全问题。

您可以使用eipjmpcall修改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