assembly - popl %esp 的替代方案

标签 assembly stack cpu-registers x86

在第 3.4.2 节中,IA32 popl 指令被描述为从 将堆栈顶部移动到目标寄存器,然后递增堆栈 指针。所以,如果我们有一条 popl REG 形式的指令,它将是等价的 代码顺序:

movl (%esp),REG //Read REG from stack
addl $4,%esp //Increment stack pointer

A.根据问题4.7中的分析,这段代码顺序是否正确 描述指令 popl %esp? 的行为解释一下。

B.如何重写代码序列才能正确描述两者 REG 是 %esp 以及任何其他寄存器的情况?

问题4.7:

下面的汇编代码函数让我们可以确定指令的行为 IA32 的 popl %esp:

1 .text
2 .globl poptest
3 poptest:
4 pushl %ebp
5 movl %esp, %ebp
6 pushl $0xabcd Push test value
7 popl %esp Pop to stack pointer
8 movl %esp, %eax Set popped value as return value
9 leave Restore stack and frame pointers
10 ret

我们发现这个函数总是返回0xabcd。这对 popl %esp 的行为意味着什么?还有哪些其他 Y86 指令会具有完全相同的行为?


对于第一个问题中的代码序列是否正确描述了指令 popl %esp 的行为,我一直很困惑。起初我以为是的,因为它从堆栈中获取 REG,就像 popl 一样返回值(我可能在这一点上是错误的),然后它将 esp 增加 4 以从堆栈中删除该实例。

但后来我遇到了这样一句话:“在旧堆栈顶部的数据写入目标之前,popl %esp 指令会递增堆栈指针。”

如果是这种情况,那么在将值放入目标寄存器之前应该先将 esp 加 4,从而使得

movl (%esp),REG //Read REG from stack
addl $4,%esp //Increment stack pointer

popl %esp 的错误表示。

任何人都可以澄清它是否确实没有正确描述行为或 popl %esp?

最佳答案

事实上,这与 pop 是错误的等价形式。有趣的是,这也是英特尔在官方指令集引用中使用的一个。但至少他们确实在文本中把事情说清楚了。更好的等效代码是:

leal 4(%esp), %esp ; use lea to preserve flags (thanks to @Sparky)
movl -4(%esp), REG
然而,这只是逻辑上的等价物,因为实际上有人(例如中断或信号处理程序)可能会破坏两条指令之间堆栈上的值。原始代码不会遇到这个问题。

请注意,这也适用于内存操作数,手册中说:“如果 ESP 寄存器用作寻址内存中目标操作数的基址寄存器,则 POP 指令计算操作数的有效地址在它增加 ESP 寄存器之后。”。我们也解决了这个问题。

关于assembly - popl %esp 的替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26389596/

相关文章:

assembly - Z80 (TI-83+) 在 CALL 上停止工作

visual-c++ - VC++ SSE 代码生成 - 这是编译器错误吗?

java - Greedy Best First Search 使用队列还是堆栈?

java - 找到...对象,但使用 Java 堆栈预期出现 myClass 错误

linux - 确定线程的堆栈大小和位置

java - 访问 cpu 寄存器

emulation - 为什么6502的中断标志的初始状态为1?

assembly - 反转 MIPS 汇编中数字的位

assembly - 汇编语言有多少种

assembly - 8086 (x86-16) CPU 堆栈指针的初始值是多少?