对寻址模式的混淆 - 寄存器本身在 () 之外如何作为ADDRESS_OR_OFFSET常量工作?

Confusion about addressing modes - how does a register by itself outside () work as an ADDRESS_OR_OFFSET constant?

提问人:Enlico 提问时间:8/23/2023 最后编辑:Peter CordesEnlico 更新时间:8/23/2023 访问量:53

问:

《从头开始编程》的第 3 章中,我读到了

内存地址引用的一般形式是这样的:

ADDRESS_OR_OFFSET(%BASE_OR_OFFSET, %INDEX, MULTIPLIER)

所有字段都是可选的。要计算地址,只需执行以下计算:

FINAL ADDRESS = ADDRESS_OR_OFFSET + %BASE_OR_OFFSET + MULTIPLIER * %INDEX

ADDRESS_OR_OFFSET并且都必须是常量,而另外两个必须是寄存器。如果其中一个部分被遗漏,它只是在等式中被零替换。MULTIPLIER

现在,我假设用零代替是一个错别字,因为如果 的默认值为 0,那么 的值将无关紧要,因为乘积无论如何都会为零(确实)。我猜其他 0 个是默认值 3?MULTIPLIER%INDEX

尽管如此,最让我困惑的是,从上面的描述中,我了解到括号和逗号具有确定我们所写内容的哪些部分映射到寻址的 4 个“操作数”的功能。

但是,在下一章中,我读到了

例如,以下代码将堆栈顶部的任何内容移动到:%eax

movl (%esp), %eax

如果我们只是这样做

movl %esp, %eax

%eax只会将指针放在堆栈的顶部,而不是顶部的值。

但我不明白为什么。我的意思是

  • 鉴于上面的表达,我会说FINAL ADDRESS

    • 如果我们放在括号里,它将扮演 的角色,默认为 0 和 1,%esp%BASE_OR_OFFSETADDRESS_OR_OFFSET%INDEXMULTIPLIER
    • 如果我们不放在括号里,它将扮演 的角色,默认为 0 和 1,%espADDRESS_OR_OFFSET%BASE_OR_OFFSET%INDEXMULTIPLIER

    总和仍然是一样的。

  • 此外,常数如何?%esp

    • 也许我犯了一个错误,认为它不是恒定的,因为我想到了 ?%esp
    • 如果是这样的话,并且是常量,因为是物理固定寄存器的名称,那么在这种情况下,什么是非常量?%esp
程序集 x86 术语 att 寻址模式

评论

0赞 Erik Eidt 8/23/2023
这本书似乎做了一个非常笼统的声明,尽管它限定了它是关于内存(地址)引用的。还有其他寻址模式,例如寄存器直接,这是 w/o () 中的寻址模式。%espmovl

答:

3赞 Peter Cordes 8/23/2023 #1

正确,默认乘数为 。1


movl %esp, %eax 根本没有使用内存寻址模式。它是一个寄存器直接操作数,因此它在语法上与(来自绝对地址的负载)不同。mov symbol_name, %eax

有一个寄存器,但它不在里面,所以语法不适用。()disp(base,idx,scale)

在机器码中,ModRM 字节的 2 位“mode”字段用于编码它是寄存器操作数而不是内存。(其他 3 种编码选择无位移的内存,vs. disp8 vs. disp32:https://wiki.osdev.org/X86-64_Instruction_Encoding#ModR.2FM_and_SIB_bytes。另请参阅 rbp not allowed as SIB base?,了解允许不带寄存器的有趣特殊情况,并使 SIB 字节可选,以节省简单寻址模式的机器代码大小。使用 ModR/M.mode = 时,该字段只是一个简单的寄存器编号。同样,在汇编语言中,当您使用裸寄存器名称时,您只是直接获取寄存器操作数,而不是将其用作访问内存的地址。0b11disp3211

(我不确定这是否是一个有用的类比,但我认为有用的一点是,即使在 x86 机器代码中,寄存器操作数也与内存操作数不同。它们在质量上是不同的,需要加以区分。


还相关: