寄存器%eiz是什么?

62
在我使用objdump工具转储出的以下汇编代码中:
lea    0x0(%esi,%eiz,1),%esi

寄存器%eiz是什么? 上面的代码是什么意思?


1
你可能会觉得http://sourceware.org/ml/binutils/2009-01/msg00081.html很有趣。 - Pascal Cuoq
3个回答

64

请参阅为什么GCC要使用LEA EIZ指令?:

显然,%eiz是一个伪寄存器,始终计算为零(类似于MIPS上的r0)。

...

最终,我在binutils专家Ian Lance Taylor的邮件列表中发现了答案。有时,GCC会将NOP指令插入代码流中以确保正确对齐等。 NOP指令需要一个字节,因此您可以认为只需添加所需数量即可。但是根据Ian Lance Taylor的说法,对于芯片来说,执行一个长指令比执行多个短指令更快。因此,他们不是插入七个NOP指令,而是使用一个奇怪的LEA指令,它使用了七个字节,并且在语义上等同于NOP。


2
聪明的人 :) 感谢您的回答!因此,上面的代码只是 nop 的更长版本 :P - Chang Hyun Park
7
更具体地说,它是一个占位符,用于不必要的SIB字节,该字节编码了一种没有索引的寻址模式。 - Peter Cordes

27

(虽然很晚才介入,但这似乎是一个有趣的补充):它根本不是一个寄存器,而是英特尔指令编码的一个怪癖。当使用ModRM字节从内存加载时,有3个位用于寄存器字段以存储8个可能的寄存器。但是ESP(堆栈指针)“应该”在的位置被处理器解释为“此指令后跟一个SIB字节”(即它是扩展寻址模式,而不是对ESP的引用)。由于只有作者知道的原因,GNU汇编器一直将这个“零否则会有一个寄存器”的地方表示为“%eiz”寄存器。英特尔语法直接省略它。


2
binutils 只对冗余的SIB字节执行此操作(即基础不是E/RSP,没有索引)。它使用(%esp) / (%rsp)而不是(%esp, %eiz, 1) - Peter Cordes
这是GNU汇编器的适当输入吗,还是只是反汇编器的功能? - Kaz
1
可能提到 IZ 的原因是它代表“索引零”。 - CherryDT

16

Andy Ross提供了更多的根本原因,但不幸的是在技术细节上他是错误的或者至少是令人困惑的。确实,只使用(%esp)作为有效地址无法仅使用ModR/M字节进行编码,因为它不会被解码为(%esp),而是用于表示也包括一个SIB字节。然而,%eiz 伪寄存器并不总是与SIB字节一起使用来表示SIB字节已使用。

SIB字节(比例/索引/基址)由三个部分组成:索引(一个寄存器,例如%eax%ecx,应用比例),比例(1到8的2的幂,索引寄存器乘以该比例),以及基址(另一个将缩放后的索引加上的寄存器)。这就是允许指令add %al,(%ebx,%ecx,2)(机器代码:00 04 4b -- 操作码,modr/m,sib(请注意,即使使用了SIB字节,也没有%eiz寄存器))(或在Intel语法中,“add BYTE PTR [ecx*2+ebx], al”)。

然而,%esp不能用作SIB字节中的索引寄存器。Intel没有允许这个选项,而是添加了一个使用基址寄存器的选项,不进行缩放或索引。因此,为了消除add %al,(%ecx)(机器代码:00 01 -- 操作码,modr/m)和add %al,(%ecx)(机器代码:00 04 21 -- 操作码,modr/m,sib)之间的歧义,使用备用语法add %al,(%ecx,%eiz,1)(或Intel语法: add BYTE PTR [ecx+eiz*1],al)。

正如 Sinan 提供的文章所解释的那样,这个特定的指令(lea 0x0(%esi,%eiz,1),%esi)仅被用作多字节nop(相当于esi = &*esi),这样就只需要执行一个类似nop的指令,而不是多个nop指令。


3
顺便提一句,ESP不能作为索引的原因是(%esp)(%esp,%esp,1..8)更有用。由于无法编码基址为ESP且不带SIB字节,因此需要某种方式指定没有索引。(因为没有基址会要求使用disp32,而他们不想要求使用disp32 = 0(,%esp,1)使得ESP相对寻址变得过于昂贵。) - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接