objdump
工具转储出的以下汇编代码中:lea 0x0(%esi,%eiz,1),%esi
寄存器%eiz
是什么? 上面的代码是什么意思?
objdump
工具转储出的以下汇编代码中:lea 0x0(%esi,%eiz,1),%esi
寄存器%eiz
是什么? 上面的代码是什么意思?
显然,
%eiz
是一个伪寄存器,始终计算为零(类似于MIPS上的r0
)。
...
最终,我在binutils专家Ian Lance Taylor的邮件列表中发现了答案。有时,GCC会将NOP指令插入代码流中以确保正确对齐等。 NOP指令需要一个字节,因此您可以认为只需添加所需数量即可。但是根据Ian Lance Taylor的说法,对于芯片来说,执行一个长指令比执行多个短指令更快。因此,他们不是插入七个NOP指令,而是使用一个奇怪的LEA指令,它使用了七个字节,并且在语义上等同于NOP。
(虽然很晚才介入,但这似乎是一个有趣的补充):它根本不是一个寄存器,而是英特尔指令编码的一个怪癖。当使用ModRM字节从内存加载时,有3个位用于寄存器字段以存储8个可能的寄存器。但是ESP(堆栈指针)“应该”在的位置被处理器解释为“此指令后跟一个SIB字节”(即它是扩展寻址模式,而不是对ESP的引用)。由于只有作者知道的原因,GNU汇编器一直将这个“零否则会有一个寄存器”的地方表示为“%eiz”寄存器。英特尔语法直接省略它。
(%esp)
/ (%rsp)
而不是(%esp, %eiz, 1)
。 - Peter CordesAndy 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指令。
(%esp)
比(%esp,%esp,1..8)
更有用。由于无法编码基址为ESP且不带SIB字节,因此需要某种方式指定没有索引。(因为没有基址会要求使用disp32,而他们不想要求使用disp32 = 0(,%esp,1)
使得ESP相对寻址变得过于昂贵。) - Peter Cordes