assembly - 为什么在使用 PUSH 或 POP 指令时不鼓励使用 ESP 寄存器?

标签 assembly x86 stack-pointer

我正在查看 C 代码,其中我必须弄清楚在特定程序跟踪中使用的寄存器数量。每当遇到 push 或 pop 命令时,代码都忽略了存储 ESP 寄存器。我还引用了 X86 指令,它写在那里“ESP,堆栈指针,不应使用”。为什么??

最佳答案

在 x86 上,ESP 是堆栈指针。最初,在 16 位 8088 和 8086 处理器上,该寄存器简称为 SP,用于Stack Pointer。在 386 处理器中添加 32 位支持时,E 前缀(表示“扩展”)被添加到所有寄存器名称中,因此它变成了 ESP。堆栈指针的位宽始终与当前模式下处理器的 native 字长相同。也就是说,如果您在 32 位保护模式下执行,堆栈指针将为 32 位宽并存储在 ESP 中。如果您在 16 位实模式下执行,堆栈指针将为 16 位宽并存储在 SP 中。

x86 架构的 64 位扩展(也称为 AMD64、x86-64 或简称 x64)将寄存器扩展到 64 位并添加了 R 前缀。因此,RSP 寄存器包含堆栈指针,在长(64 位)模式下执行时,该指针为 64 位宽。

尽管该寄存器在概念上与其他寄存器(EAXECXEDXEBXESIEDIEBP),它不能以等效的方式使用。它被设计为用于保存堆栈指针,不能用作通用寄存器。

您没有显式压入或弹出堆栈指针的原因是因为这是由其他指令隐式完成的。事实上,PUSHPOP 指令是操作堆栈指针的指令,因为它们将内容压入和弹出到堆栈。

在 x86 上,堆栈总是在内存中向下增长,所以 PUSH 会从堆栈指针中减去适当数量的字节(根据操作数的大小),而 POP 将添加适当数量的字节。

CALLRET指令还隐式操作堆栈指针。您可以通过阅读 Intel x86 体系结构手册找到更多详细信息,available here . 中还有许多其他可用资源标记维基。

只有在 ADDSUB 中将其用作目标操作数时,您才会看到 ESP 寄存器被显式操作操作说明。这些通常通过优化编译器插入函数的序言和结尾,根据需要递增或递减堆栈指针以腾出额外空间来存储值或清理堆栈。它们的功能类似于PUSHPOP,不同之处在于它们可以具有连续多次压入和弹出的效果。例如:

push eax
push eax
push eax
push eax
...
pop  eax
pop  eax
pop  eax
pop  eax

可以简单地替换为:

sub esp, 16
...
add esp, 16

(假设您实际上并没有尝试存储EAX 的值,而只是使用PUSH 在堆栈上腾出空间).

关于assembly - 为什么在使用 PUSH 或 POP 指令时不鼓励使用 ESP 寄存器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12503850/

相关文章:

c++ - 如何在 __asm 中使用变量?

c++ - 德州仪器数字信号处理器 : interfacing C++ and assembly

assembly - Linux内核0.01引导加载程序使用rep movw,而不是rep movsw?这实际上是重复普通的 MOV 吗?

c++ - ESP 调用约定问题 C++

arm - ARM Cortex-M、MSP 或 PSP 中复位后使用哪个堆栈?

linux - Linux ARM 上程序寄存器和堆栈的初始状态

assembly - 写入翻译后备缓冲区

assembly - strcmp 评估哪些寄存器? x86 汇编

c++ - 嵌入式 asm 代码中的运行时检查失败 #0

gcc - 为什么objdump的汇编编码不同?