在 x86 32 位保护模式下,使用 PUSH/POP 指令时,我们还可以使用 SP 而不是 ESP 寄存器吗?

问题描述

不知道什么时候用SP寄存器,什么时候用ESP寄存器,另外我想知道什么时候用ESP寄存器,是不是总是减4不能减2?

解决方法

技术上 SP 可以在 32 位模式下用于 pushpop,但它需要修改一个通常无法更改的标志。

在 pmode 中使用 SP 还是 ESP 由 SS 引用的段描述符中的 B 标志控制。通常在用户模式代码中,这不是您有权访问的内容,因此您无法更改它。

默认操作数大小和默认地址大小有类似但独立的机制(不是与堆栈地址大小相同,普通地址大小用于{{ 1}} 和 push [mem])。这是 3 件独立的事情,见下文。

使用ESP寄存器时,是不是总是减4,不能减2

推送/弹出 pop [mem] 是可能的,甚至很容易,即使在 64 位模式下也是如此。为此,您只需要操作数大小覆盖前缀 66h。例如,word 在 32 位甚至 64 位模式下都是绝对可编码的(尽管我不建议使用它)。这与是否使用 SP 或 ESP 无关。

所以有三种不同的尺寸在起作用,其中两种有一个覆盖前缀可以改变它:

  1. 操作数大小,压入 push dxword,这决定了堆栈指针移动 2 还是 4。通常在 32 位模式下,默认大小将设置为 32 位,但操作数大小覆盖前缀可用于在 32 位模式(甚至 64 位模式)下对 dword 进行编码。
  2. 地址大小,用于带有显式内存操作数的 push dxpush,而不是用于隐式堆栈位置。地址大小覆盖前缀可用于编码,例如 pop,两个前缀可一起用于编码 push dword [bx]。在 64 位模式下,地址大小覆盖前缀会导致地址被解释为 32 位地址,而不是 16 位地址,因此 push word [bx] 是不可能的。
  3. 堆栈地址大小,仅由堆栈段描述符中的 B 标志决定,没有覆盖前缀。
,

SP 是一个 16bit 的寄存器,在 32bit 模式下你必须使用 ESP。

pushpop 指令始终在 4 字节块中工作,因此您不能将堆栈指针减少 2。您可以手动使用 { {1}},但随后您会弄乱堆栈对齐方式,这可能会导致显着的性能损失(尤其是在堆栈上处理浮点值时,iirc)

所以一般来说,除非您有一个非常具体的目标,否则在 32 位模式下,您使用 ESP 并将堆栈视为一系列 4 字节值