【发布时间】:2011-02-02 22:55:45
【问题描述】:
在我使用objdump 转储的以下汇编代码中:
lea 0x0(%esi,%eiz,1),%esi
什么是寄存器%eiz?上面的代码是什么意思?
【问题讨论】:
标签: assembly x86 gnu-assembler objdump
在我使用objdump 转储的以下汇编代码中:
lea 0x0(%esi,%eiz,1),%esi
什么是寄存器%eiz?上面的代码是什么意思?
【问题讨论】:
标签: assembly x86 gnu-assembler objdump
显然
%eiz是一个伪寄存器,它始终只计算为零(就像 MIPS 上的r0)。
...
我最终找到了 binutils 大师 Ian Lance Taylor 的邮件列表帖子,其中揭示了答案。有时 GCC 会在代码流中插入 NOP 指令以确保正确对齐和类似的东西。 NOP 指令占用一个字节,因此您会认为可以根据需要添加任意数量的字节。但根据 Ian Lance Taylor 的说法,芯片执行一条长指令比执行许多短指令要快。因此,它们没有插入 7 个 NOP 指令,而是使用了一个 bizarro LEA,它占用了 7 个字节并且在语义上等同于 NOP。
【讨论】:
Andy Ross 提供了更多基本推理,但不幸的是,他错了,或者至少对技术细节感到困惑。确实,仅(%esp) 的有效地址不能仅使用 ModR/M 字节进行编码,因为它不会被解码为 (%esp),它用于表示还包括 SIB 字节。但是,%eiz 伪寄存器并不总是与 SIB 字节一起使用来表示使用了 SIB 字节。
SIB 字节(标度/索引/基址)包含三个部分:索引(应用标度的寄存器,例如 %eax 或 %ecx),标度(从索引寄存器乘以 1 到 8)和基数(添加到缩放索引的另一个寄存器)。这就是允许诸如 add %al,(%ebx,%ecx,2) 之类的指令(机器代码:00 04 4b -- opcode, modr/m, sib(注意没有 %eiz 寄存器,即使使用了 SIB 字节))(或者在 Intel 语法中,“添加BYTE PTR [ecx*2+ebx], al")。
但是,%esp 不能用作 SIB 字节中的索引寄存器。英特尔没有允许此选项,而是添加了一个选项,可以按原样使用基址寄存器,而无需缩放或索引。因此,为了区分add %al,(%ecx)(机器代码:00 01 -- opcode,modr/m)和add %al,(%ecx)(机器代码:00 04 21 -- opcode,modr/m,sib)的情况,替代语法改为使用 add %al,(%ecx,%eiz,1)(或 Intel 语法:add BYTE PTR [ecx+eiz*1],al)。
正如思南链接的文章中所解释的,这条特定指令(lea 0x0(%esi,%eiz,1),%esi)仅用作多字节 nop(相当于esi = &*esi),因此只需执行一条类似 nop 的指令而不是多个 nop 指令。
【讨论】:
(%esp) 是一种比(%esp, %esp, 1..8) 更有用的寻址模式。由于您无法在没有 SIB 字节的情况下对 base=ESP 进行编码,因此您需要某种方式来指定无索引。 (因为没有基地需要 disp32,而且他们不想要求 disp32=0( , %esp, 1) 使 ESP 相对寻址变得不合理地昂贵。)
(游戏很晚,但这似乎是一个有趣的补充):它根本不是一个寄存器,它是 Intel 指令编码的一个怪癖。当使用 ModRM 字节从内存中加载时,寄存器字段有 3 位用于存储 8 个可能的寄存器。但是,ESP(堆栈指针)“将”所在的位置被处理器解释为“SIB 字节遵循该指令”(即,它是扩展寻址模式,而不是对 ESP 的引用)。由于只有作者知道的原因,GNU 汇编器总是将这个“寄存器本来应该是零”表示为“%eiz”寄存器。英特尔语法只是删除它。
【讨论】:
(%esp) / (%rsp) 而不是(%esp, %eiz, 1)。