什么时候应该在x86中使用尺寸指令?
什么时候在x86中使用大小指令似乎有点含糊。 This x86 assembly guide说以下内容:什么时候应该在x86中使用尺寸指令?
通常,的数据项中的一个给定的存储器地址 预期的大小可以从汇编代码指令,其中它是 引用来推断。例如,在所有上述指令中,可以根据寄存器 操作数的大小来推断存储器区域的大小。当我们加载一个32位寄存器时,汇编器可能会推断我们所指的内存区域是4个字节宽。 当我们将一个字节寄存器的值存储到存储器时,汇编器可能会推断出我们希望地址引用内存中的单个字节 。
他们给出的例子非常微不足道,比如将立即值移入寄存器。
但是关于更复杂的情况,如下列内容:
mov QWORD PTR [rip+0x21b520], 0x1
在这种情况下,是不QWORD PTR大小指令多余的,因为,按照上面的指导,我们可以假设,我们要由于RIP为8字节,因此将8个字节移入目标寄存器? x86架构上尺寸指令的权威规则是什么?我无法在任何地方找到答案,谢谢。
更新:正如Ross指出的那样,上例中的目的地不是寄存器。以下是一个相关的例子:
mov esi, DWORD PTR [rax*4+0x419260]
在这种情况下,不能把它假设我们要移动4个字节,因为ESI为4个字节,使得DWORD PTR指令冗余?
它变得更糟,汇编语言是由汇编程序定义的,该程序读取/解释/解析它。特别是x86,但是作为一般规则,任何两个汇编程序没有技术上的理由使得相同的目标程序具有相同的汇编语言,它们往往是相似的,但不一定非要。
你陷入了几个陷阱,首先关于你正在使用的汇编程序使用的特定语法关于size指令,然后第二,是否有默认值。我的建议总是使用size指令(或者如果有独特的指令助记符),那么你不必担心它是正确的吗?
我通常会做相反的事情:我最初从不使用任何,只有在汇编程序抱怨时才添加它们。如果找不到,每个汇编程序都会报错。特别是在我使用最多的汇编程序(Delphi的BASM)中,变量和结构成员具有指定的大小,所以很少有必要。使编写代码更容易,IMO。我使用过NASM,FASM,YASM,BASM,TASM,MASM,如果有必要,他们都会抱怨。 –
我想我听说过一些有缺省操作数大小的令人讨厌的x86汇编程序。也许是内置于emu8086的? –
RIP
或地址中的任何其他寄存器只与寻址模式有关,而不是数据传输的宽度。内存引用[rip+0x21b520]
可以使用1,2,4或8字节访问,常数值0x01
也可以是1到8个字节(0x01
与0x00000001
等相同)。因此在这种情况下,操作数大小必须明确提及。
与寄存器为源或目的地,操作数大小是隐式的:如果,比如说,EAX
被使用时,数据是32位或4个字节:
mov [rip+0x21b520],eax
当然,在非常漂亮的AT语法,操作数大小被标记为指令助记符的后缀(l
这里)。
movl $1, 0x21b520(%rip)
你是对的;这是相当模糊的。假设我们谈论的是英特尔语法,确实如此,您可以使用大小指令来避开而不是。任何时候汇编器都可以自动计算出来,它们是可选的。例如,在指令
mov esi, DWORD PTR [rax*4+0x419260]
的DWORD PTR符是可选的确切原因,你想:汇编能弄清楚,它是将一个DWORD大小的值,因为该值被移入DWORD大小的寄存器。
类似地,在
mov rsi, QWORD PTR [rax*4+0x419260]
的QWORD PTR说明符是可选的确切相同的原因。
但它并不总是可选的。考虑你的第一个例子:
mov QWORD PTR [rip+0x21b520], 0x1
这里,QWORD PTR说明符不可选。如果没有它,汇编程序不知道您想要从地址rip+0x21b520
开始存储什么大小的值。 0x1
应该作为BYTE存储吗?扩展为WORD?一个DWORD? QWORD?有些汇编器可能会猜测,但是如果没有明确指定你想要的结果,你不能确定正确的结果。
换句话说,当值在寄存器操作数中时,大小说明符是可选的,因为汇编程序可以根据寄存器的大小计算出大小。但是,如果处理立即值或内存操作数,则可能需要使用大小说明符来确保获得所需的结果。
个人而言,我更喜欢总是包括我写代码时的大小。这是一些更多打字的角色,但它迫使我考虑它并明确说明我想要的。如果我搞砸了,编写了一个不匹配的代码,那么汇编程序会对我大声尖叫,这已经不止一次地发现了错误。我也认为在那里增强可读性。所以在这里我同意old_timer,虽然他的观点似乎有些不受欢迎。
反汇编器在其输出中也往往是冗长的,包括大小说明符,即使它们是可选的。 Hans Passant在评论中提出理论,这是为了保持与老派装配工的向后兼容性,这些总是需要这些,但我不确定这是否属实。它可能是其中的一部分,但根据我的经验,反汇编程序往往在不同的方式下很难做出很多,我认为这只是为了更容易分析您不熟悉的代码。
请注意,AT & T语法使用略有不同的机智。它不是将大小作为操作数的前缀,而是将其后缀添加到指令助记符中:字节为b
,字为w
,双字为l
,双字为q
。所以,前三次的例子变成:
movl 0x419260(,%rax,4), %esi
movq 0x419260(,%rax,4), %rsi
movq $0x1, 0x21b520(%rip)
此外,前两个指示,l
和q
前缀是可选的,因为汇编器可以推断出合适的大小。在最后一条指令中,就像在英特尔语法中一样,前缀是非可选的。因此,与英特尔语法一样,ATT语法也是如此,只是大小说明符的不同格式。
一些汇编程序,如MASM或TASM或Delphi的内置汇编程序,可以声明变量或结构成员具有一定的大小。在这些汇编程序中,使用这些变量作为目标或源操作数也使得不需要使用显式大小指令。我不会称这种模糊的,只是,好的,有时难以阅读。 –
是的,高级语言风格的汇编器使其变得更加复杂。我考虑过提及这一点,但决定的答案不再需要。 :-)它遵循相同的一般原则,即如果汇编程序可以推断大小,则不需要明确指定它。但是只有类型感知的汇编器才能进行这些推导,而且这些并不是经常用于我的经验。 –
我最常用的是一个真正的(内置)汇编程序,但是结构和变量是用语言(Object Pascal)定义的。这使得事情变得非常简单,并且比外部汇编器更好用(我也这么做了,只是为了好玩)。事实上,如果汇编程序可以推导出大小,则不需要使用指令。我通常不使用任何,只有在汇编程序抱怨时才添加它们。 –
RIP不是目标寄存器。目的地根本不是寄存器,它是一个内存位置。该指令将值1存储在内存中的地址“RIP + 0x21b520”。 –
@RossRidge啊,我从来没有这样看过。谢谢,这为我澄清了一点,但我已经看到了目的地其实是寄存器的其他情况。我会更新这个问题。 – Hello
你的第二个例子中不需要'dword ptr'。 – interjay