什么时候应该在x86中使用大小指令?

Hel*_*llo 5 x86 assembly x86-64

在x86中何时使用大小指令似乎有点模棱两可。此x86组装指南中的内容如下:

通常,可以从引用该数据项的汇编代码指令中推断出该数据项在给定存储地址处的预期大小。例如,在所有上述指令中,可以从寄存器操作数的大小推断出存储区域的大小。当我们加载32位寄存器时,汇编程序可以推断出我们所指的内存区域为4字节宽。当我们将一个字节的寄存器的值存储到内存中时,汇编程序可以推断出我们希望地址引用内存中的单个字节。

他们给出的例子很简单,例如将立即数移到寄存器中。
但是,如何处理更复杂的情况,例如:

mov    QWORD PTR [rip+0x21b520], 0x1
Run Code Online (Sandbox Code Playgroud)

在这种情况下,QWORD PTR size指令不是多余的,因为根据上述指南,可以假定由于RIP为8字节,所以我们要将8字节移入目标寄存器吗?x86体系结构上的size指令的最终规则是什么?谢谢,我在任何地方都找不到答案。

更新:正如罗斯指出,以上示例中的目的地不是寄存器。这是一个更相关的示例:

mov    esi, DWORD PTR [rax*4+0x419260] 
Run Code Online (Sandbox Code Playgroud)

在这种情况下,不能假设由于ESI为4个字节而要移动4个字节,从而使DWORD PTR指令变得多余了吗?

Cod*_*ray 6

你说得对; 这是相当模糊的。假设我们谈论的是 Intel 语法,确实,您通常可以使用大小指令。只要汇编器可以自动计算出它,它们就是可选的。例如,在指令中

mov    esi, DWORD PTR [rax*4+0x419260] 
Run Code Online (Sandbox Code Playgroud)

DWORD PTR 说明符是可选的,其原因正是您所想象的:汇编器可以确定要移动 DWORD 大小的值,因为该值正在移动到 DWORD 大小的寄存器中。

同样,在

mov    rsi, QWORD PTR [rax*4+0x419260] 
Run Code Online (Sandbox Code Playgroud)

出于完全相同的原因,QWORD PTR 说明符是可选的。

但这并不总是可选的。考虑你的第一个例子:

mov    QWORD PTR [rip+0x21b520], 0x1
Run Code Online (Sandbox Code Playgroud)

此处,QWORD PTR 说明符不是可选的。如果没有它,汇编器就不知道要从地址 开始存储多大的值rip+0x21b520。应该0x1存储为 BYTE 吗?扩展到一个WORD?一个双字?QWORD?一些汇编程序可能会猜测,但如果没有明确指定您想要的结果,则无法保证正确的结果。

换句话说,当值位于寄存器操作数中时,大小说明符是可选的,因为汇编器可以根据寄存器的大小计算出大小。但是,如果您正在处理立即值或内存操作数,则可能需要大小说明符以确保获得所需的结果。

就我个人而言,我更喜欢在编写代码时始终包含大小。虽然多打了几个字符,但它迫使我思考并明确说明我想要什么。如果我搞砸了并且编写了不匹配的代码,那么汇编器就会对我大声尖叫,这已经不止一次地捕获了错误。我还认为将它放在那里可以增强可读性。所以在这里我同意old_timer,尽管他的观点似乎有些不受欢迎。

反汇编程序的输出也往往很冗长,包括大小说明符,即使它们是可选的。Hans Passant 在评论中推测这是为了保持与总是需要这些的老式汇编器的向后兼容性,但我不确定这是真的。这可能是其中的一部分,但根据我的经验,反汇编程序往往在很多不同的方面都很冗长,我认为这只是为了更容易分析您不熟悉的代码。

请注意,AT&T 语法使用的策略略有不同。它不是将大小写为操作数的前缀,而是在指令助记符中添加后缀:bfor byte、wfor word、lfor dword 和qfor qword。因此,前面的三个例子就变成了:

movl    0x419260(,%rax,4), %esi
movq    0x419260(,%rax,4), %rsi
movq    $0x1, 0x21b520(%rip)
Run Code Online (Sandbox Code Playgroud)

同样,在前两条指令中,lq前缀是可选的,因为汇编器可以推导出适当的大小。在最后一条指令上,就像英特尔语法一样,前缀是非可选的。因此,AT&T 语法与 Intel 语法相同,只是大小说明符的格式不同。

  • 一些汇编器,如 MASM 或 TASM 或 Delphi 的内置汇编器,可以将变量或结构成员声明为具有一定的大小。在这些汇编器中,使用这些变量作为目标或源操作数也使得无需使用显式大小指令。我不会说这是模棱两可的,只是,好吧,有时更难阅读。 (2认同)

old*_*mer -1

更糟糕的是,汇编语言是由汇编器定义的,汇编器是读取/解释/解析它的程序。尤其是 x86,但作为一般规则,同一目标的任何两个汇编器都没有相同汇编语言的技术原因,它们往往相似,但不一定如此。

您陷入了几个陷阱,首先是您所使用的汇编器所使用的关于大小指令的特定语法,其次是是否有默认值。我的建议是始终使用大小指令(或者如果有独特的指令助记符),那么您永远不必担心它,对吧?