在我使用objdump
转出的以下汇编代码中:
lea 0x0(%esi,%eiz,1),%esi
什么是注册%eiz
?前面的代码是什么意思?
答案 0 :(得分:54)
显然
%eiz
是一个伪寄存器,它在任何时候都只评估为零(如MIPS上的r0
)。
...
我最终找到了binutils大师Ian Lance Taylor的邮件列表帖子,揭示了答案。有时GCC会将NOP指令插入到代码流中,以确保正确对齐和类似的东西。 NOP指令占用一个字节,因此您可以认为可以根据需要添加任意数量的字节。但根据Ian Lance Taylor的说法,芯片执行一条长指令要比许多短指令快。因此,它们不是插入七条NOP指令,而是使用一个奇数LEA,它占用了7个字节,在语义上等同于NOP。
答案 1 :(得分:23)
(游戏很晚,但这似乎是一个有趣的补充):它根本不是一个注册,它是英特尔指令编码的一个怪癖。当使用ModRM字节从存储器加载时,寄存器字段有3位用于存储8个可能的寄存器。但是,ESP(堆栈指针)“将”所在的位置被处理器解释为“SIB字节遵循该指令”(即,它是扩展寻址模式,而不是对ESP的引用)。由于作者只知道的原因,GNU汇编器总是将“零寄存器本来就是零”表示为“%eiz”寄存器。英特尔语法只是放弃了它。
答案 2 :(得分:14)
Andy Ross提供了更多潜在的推理,但不幸的是错误或至少混淆了技术细节。确实,只有(%esp)
的有效地址不能仅用ModR / M字节编码,而不是被解码为(%esp)
,它用于表示还包括SIB字节。但是,%eiz
伪寄存器并不总是与SIB字节一起使用来表示使用了SIB字节。
SIB字节(比例/索引/基数)有三个部分:索引(比例应用于%eax
或%ecx
的寄存器),比例(a索引寄存器乘以1到2的2的幂,以及基数(添加到缩放索引的另一个寄存器)。这允许使用add %al,(%ebx,%ecx,2)
(机器代码:00 04 4b
- 操作码,modr / m,sib(即使使用了SIB字节也注意没有%eiz寄存器))(或在英特尔中)语法,“添加BYTE PTR [ecx * 2 + ebx],al”)。
但是,%esp
不能用作SIB字节中的索引寄存器。而不是允许这个选项,英特尔改为添加一个选项来使用基址寄存器,没有缩放或索引。因此,消除add %al,(%ecx)
(机器代码:00 01
- 操作码,modr / m)和add %al,(%ecx)
(机器代码:00 04 21
- 操作码,modr /的情况之间的歧义m,sib),替代使用了替代语法add %al,(%ecx,%eiz,1)
(或者用于英特尔语法:add BYTE PTR [ecx+eiz*1],al
)。
正如Sinan链接的文章所解释的那样,这个特定指令(lea 0x0(%esi,%eiz,1),%esi
)仅用作多字节nop(相当于esi = &*esi
),因此只有一条nop式指令必须执行而不是多个nop指令。