在3.4.2节中,IA32 popl指令被描述为复制结果 堆栈顶部到目标寄存器,然后递增堆栈 指针。所以,如果我们有一个popl REG形式的指令,它将是等价的 到代码序列:
movl (%esp),REG //Read REG from stack
addl $4,%esp //Increment stack pointer
一个。根据问题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指令在堆栈旧堆栈的数据写入目标之前递增堆栈指针。"
如果是这种情况,那么在将值放入目标寄存器之前,应该将4增加到esp,这样做
movl (%esp),REG //Read REG from stack
addl $4,%esp //Increment stack pointer
popl%esp。
的错误表示有人可以澄清它是否真的没有正确描述行为或popl%esp?
答案 0 :(得分:3)
确实这是pop
的错误等价物。有趣的是,这也是英特尔在官方指令集参考中使用的。但至少他们确实在文中说清楚了。更好的等效代码是:
leal 4(%esp), %esp ; use lea to preserve flags (thanks to @Sparky)
movl -4(%esp), REG
然而,这只是一个逻辑等价物,因为实际上有人(例如中断或信号处理程序)可能会破坏两个指令之间的堆栈上的值。原始代码不会遇到这个问题。
请注意,这也适用于内存操作数,手册中说:“如果ESP寄存器用作寻址内存中目标操作数的基址寄存器,则POP指令计算操作数的有效地址在它增加ESP寄存器之后。“。我们也涵盖了这个案例。