19 x86 cpu-registers program-counter
关于x86程序集的维基百科文章称"程序员无法直接访问IP寄存器".
直接表示像mov和add这样的指令.
为什么不?这背后的原因是什么?有哪些技术限制?
Pol*_*ial 25
您无法直接访问它,因为没有合法的用例.任何指令的任意改变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
Run Code Online (Sandbox Code Playgroud)
那可能是x86的可能设计.ARM确实将其程序计数器公开为读/写为R15.不过这很不寻常.
它允许一个非常紧凑的功能序言/结语,以及使用单个指令推送或弹出多个寄存器的能力: push {r5, lr}在进入和pop {r5, pc}返回时.(将保存的链接寄存器值弹出到程序计数器中).
但是,它使得高性能/无序ARM实现不太方便,并且因为AArch64而被删除.
所以它是可能的,但是用完了其中一个寄存器.32位ARM有16个整数寄存器(包括PC),因此寄存器号需要4位才能在ARM机器代码中进行编码.另一个寄存器几乎总是被绑定为堆栈指针,因此ARM有14个通用整数寄存器.(LR可以保存到堆栈中,因此它可以用作函数体内的通用寄存器).
现代x86的大多数都是从8086继承而来的.它采用相当紧凑的可变长度指令编码设计,只有8个寄存器,每个src只需3位,dst寄存器在机器码中.
在最初的8086中,它们不是非常通用的,并且在16位模式下不可能进行SP相对寻址,因此基本上2个寄存器(SP和BP)被用于堆栈内容.这只留下了6个有点通用的寄存器,并且其中一个是PC而不是通用的,这将大大减少可用寄存器,大大增加了典型代码中的溢出/重载量.
AMD64增加了r8-r15和RIP相对寻址模式. lea rsi, [rip+whatever]和直接访问静态数据和常量的RIP相对寻址模式,是高效的与位置无关的代码所需要的.间接JMP指令完全足以写入RIP.
通过允许使用任意指令来读取或写入PC,实际上没有任何东西可以获得,因为你总是可以使用整数寄存器和间接跳转来做同样的事情.x86-64的R15与RIP完全相同,特别是对于架构作为编译器目标的性能,这几乎是纯粹的缺点.(手写的asm奇怪的东西在2000年设计AMD64时已经非常普遍了.)
因此AMD64真的是第一次有可能获得像ARM这样的完全暴露的程序计数器,但有很多很好的理由不这样做.