Assembly 推送和弹出在装配中是如何工作的

Assembly 推送和弹出在装配中是如何工作的,assembly,x86,callstack,Assembly,X86,Callstack,我对pop在汇编中实际做什么感到困惑。pop是否将值PUSHed移动到堆栈的最后一个位置(这意味着如果我们MOV在最后一个元素PUSHed之后添加一个值,则该值不适用),还是只弹出堆栈上最后一个的值(因此,同时应用于MOV和PUSH),或者它会弹出堆栈指针指向的值吗?考虑下面的代码: push $4 mov $5, -4(%esp) add $4, %esp (esp pointing to an unknown value) pop %ebp 因此,在此代码中,输入到ebp的值是4、5还是e

我对
pop
在汇编中实际做什么感到困惑。
pop
是否将值
PUSH
ed移动到堆栈的最后一个位置(这意味着如果我们
MOV
在最后一个元素
PUSH
ed之后添加一个值,则该值不适用),还是只弹出堆栈上最后一个的值(因此,同时应用于
MOV
PUSH
),或者它会弹出堆栈指针指向的值吗?考虑下面的代码:

push $4
mov $5, -4(%esp)
add $4, %esp (esp pointing to an unknown value)
pop %ebp
因此,在此代码中,输入到
ebp
的值是4、5还是
esp
指向的未知值?

后者

POP EBP
相当于

MOV EBP, [ESP]
ADD ESP, 4           ; but without modifying flags, like  LEA ESP, [ESP+4]
(在英特尔语法中-目标在左边,源在右边)

执行以下操作:

ESP:=ESP-4;对于x86-x64为8
记忆[ESP]:=
是否:

:=内存[ESP];
ESP:=ESP+4;对于x86+x64为8
如果像这样用伪代码写下机器指令的描述,那么理解机器指令的功能就容易多了。英特尔参考手册中充满了这样的伪代码, 这是值得你花时间和精力去获得它们,并为自己阅读细节。(例如,在HTML摘要和中)

关于您的具体问题:您将
$5
存储到
-4(%esp)
中是一条有效的机器指令,处理器将毫无怨言地执行它,但这确实是极不安全的编程。如果处理器恰好在该指令之后执行陷阱或中断,则处理器状态(通常)将保存在“堆栈顶部”,并将覆盖您的值。由于中断是异步发生的,因此您将看到的行为是,5美元很少丢失。这使得调试程序非常困难

“添加$4”将ESP移回push指令之前的位置。因此,您不能对弹出到ebp中的值发表任何意见,除非它是您建议的选项之一“未知”


有关为什么即使在Windows下的用户空间中,在下面编写ESP也不安全的详细信息,请参阅。(中断不会异步使用用户空间堆栈,但有一些东西可以。)在非Windows上,POSIX信号处理程序可以在用户空间ESP下方的空间上执行操作(x86-64 System V除外,在该系统中,ABI在RSP下方定义了一个128字节的“红色区域”,可以安全使用。)

因此pop实际上不会从堆栈中删除该值,只是移动它?这是正确的,但是ESP的增加有效地隐藏了它,使其无法进一步访问。理论上,中断可能会立即发生,并将堆栈的该区域重新用于存储,但我并不认为中断会再使用环3堆栈。在用户空间中,信号处理程序可以异步地对堆栈进行缓冲。在没有安装信号处理程序的Linux中,我认为您可以指望
esp
下面的空间不会被异步破坏,但ABI仍然不能保证这一点。(在x86-64 System V中,RSP下面有一个128B的红色区域,即使是信号处理程序也保证不会被破坏。但Windows没有这个区域。)而且,在任何主流操作系统中,中断都不会使用用户空间堆栈。这是不安全的:另一个线程可以在内核使用内存时修改内存,并提升其权限。当然,在内核代码中,堆栈确实可以异步关闭(如果它没有切换到与每线程内核堆栈分开的中断堆栈;不确定。不过,我认为出于这个原因,x86-64 Linux内核代码没有使用红色区域。)可能的重复
 ESP := ESP-4  ; for x86; -8 for x64
 MEMORY[ESP]:=<operandvalue>
 <operandtarget>:=MEMORY[ESP];
 ESP:=ESP+4    ; for x86; +8 for x64