27

维基百科关于 x86 汇编的文章说“程序员不能直接访问 IP 寄存器”。

直接表示使用 mov 和 add 等指令。

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

4

4 回答 4

33

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

您可以eip使用jmpcall进行编辑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
于 2011-11-30T21:51:53.360 回答
13

这可能是 x86 的一种设计。ARM 确实将其用于读/写的程序计数器公开为 R15。不过,这很不寻常。

它允许一个非常紧凑的函数序言/结尾,以及使用单个指令推送或弹出多个寄存器的能力: push {r5, lr}进入和pop {r5, pc}返回。(将链接寄存器的保存值弹出到程序计数器中)。

但是,它使高性能/无序 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-relative 寻址模式。 lea rsi, [rip+whatever],以及用于直接访问静态数据和常量的 RIP 相对寻址模式,是高效的与位置无关的代码所需要的一切。间接 JMP 指令完全足以写入 RIP。

允许使用任意指令来读取或写入 PC 并没有任何好处,因为您总是可以使用整数寄存器和间接跳转来做同样的事情。x86-64 的 R15 与 RIP 相同几乎是纯粹的缺点,特别是对于作为编译器目标的体系结构的性能而言。(到 2000 年设计 AMD64 时,手写的 asm 奇怪的东西已经非常罕见了。)

所以 AMD64 确实是 x86 第一次有可能获得像 ARM 这样的完全公开的程序计数器,但有很多充分的理由不这样做。

于 2016-12-14T18:51:51.573 回答
4

jmp将设置EIP寄存器。

此代码将 eip 设置为 00401000:

mov eax, 00401000
jmp eax ;set Eip to 00401000

并且为了得到EIP

call GetEIP
.
.
GetEIP:
mov eax, [esp]
ret
于 2015-08-25T11:42:08.440 回答
3

我认为他们的意思是不能像访问其他寄存器一样直接访问 IP 寄存器。程序员绝对可以写入 IP,例如通过发出跳转指令。

于 2011-11-30T21:53:35.930 回答