【问题标题】:Alternative to popl %esppopl %esp 的替代品
【发布时间】:2014-12-10 22:42:27
【问题描述】:

在第 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 指令在将旧堆栈顶部的数据写入目标之前递增堆栈指针。”

如果是这种情况,那么在将值放入目标寄存器之前应该发生 4 到 esp 的增量,使得

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

popl %esp 的错误表示。

谁能澄清它是否真的没有正确描述行为或popl %esp?

【问题讨论】:

    标签: assembly stack cpu-registers x86


    【解决方案1】:

    这确实是 pop 的错误等价物。有趣的是,这也是英特尔在官方指令集参考中使用的一种。但至少他们确实在文本中说明了问题。更好的等效代码是:

    leal 4(%esp), %esp ; use lea to preserve flags (thanks to @Sparky)
    movl -4(%esp), REG
    

    然而,这只是一个逻辑等价物,因为实际上有人(例如中断或信号处理程序)可能会破坏两条指令之间堆栈上的值。原代码没有这个问题。

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

    【讨论】:

    • 除了已经列出的潜在问题之外,“addl $4, %esp”还会影响 eflags 寄存器。为了弥补这一点,可以使用“leal 4(%esp), %esp”来代替它。
    • @Jester push %esp 怎么样?
    • @bluejamesbond:那将是 movl %esp, -4(%esp); leal -4(%esp), %esp,但需要注意的是。
    猜你喜欢
    • 2013-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-08
    • 2012-01-25
    • 2015-08-05
    • 2011-01-01
    • 2011-10-24
    相关资源
    最近更新 更多