例如,x86 ISA有一些特定的指令来处理与堆栈相关的操作,如push
和pop
,但堆栈完全可以在软件中实现一些内存访问操作,如mov
,甚至后者有更好的性能:为什么使用Push/Pop而不是Mov在shellcode中将数字放入寄存器中?
在ARM中,push/pop
只是内存操作的别名,参考:ARM中的Push和Pop
为什么我们需要让ISA意识到堆栈的存在?我们为什么不让硬件忘掉"堆栈"呢?把它留给软件实现?在我看来,这样做有两个好处:
- 硬件设计可以简化,
- 给软件更多的灵活性。
ISA是否可以在没有stack
概念的情况下实现,即没有push
,pop
,%rsp
,%rbp
等东西?
为什么我们需要让ISA意识到堆栈的存在?我们为什么不让硬件忘掉"堆栈"呢?把它留给软件实现?
它不需要CPU工作,这是肯定的。确实有cpu可以做到这一点,MIPS就是其中之一。虽然存在一个称为$sp
的寄存器,但它与其他通用寄存器之间没有硬件强制的区别。MIPS没有push
或pop
指令隐式地改变$sp
。要在MIPS汇编中push
寄存器到堆栈上,必须手动执行:
addiu $sp,$sp,-4
sw $s0,0($sp)
那么为什么硬件要知道堆栈呢?一个好处似乎是,在大多数有"硬件堆栈"的架构上,pop
操作往往比用内存中的值加载目标寄存器要快——即使不计算用所需地址加载源寄存器的行为也是如此。一些汇编程序员会滥用这一点,临时将堆栈重定位到一个数组(当然,数组的值是以相反的顺序存储的),并使用pop
将每个条目写入不同的寄存器。尽管需要禁用中断并将堆栈指针保存在内存中的其他地方会有一些开销,但在非常特殊的情况下,您可以从内存中复制比正常情况快一点,这取决于需要读取多少值,CPU允许的寻址模式等。