【问题标题】:Is there a way to keep a word sized immediate in a 32bit register instruction有没有办法在 32 位寄存器指令中保持字长立即数
【发布时间】:2013-06-27 02:24:13
【问题描述】:

假设以下 x86-32 指令:

add ebx,1

有(至少)两种方法来组装这个操作码:

81 c3 01 00 00 00

83 c3 01

第一个将 1 保留为 4 字节的 dword 第二个保持 1 作为一个字节

是否有一条指令将 1 保留为 2 个字节? 如果没有,为什么?

【问题讨论】:

  • 我最喜欢的是inc ebx,如果我要经常使用它,或者将常量加载到另一个reg中,然后add ebx,reg
  • 为什么不呢?因为它需要太多额外的操作码或前缀。早在 386 时,英特尔就意识到为未来的扩展留出空间可能很重要,并且没有填满编码空间。如果假设add r/m32, imm16 的操作码或前缀长了一个字节,它最终只会节省一个字节而不是两个。适合 16 位但不是 8 位的立即数非常罕见:大多数数字要么很小,要么是指针,因此它不足以证明成本合理。

标签: assembly x86 reverse-engineering nasm


【解决方案1】:

您偶然发现了 x86 指令集的一个怪癖。 Intel 在 83 的词干下包含了一组指令,其第一个操作数的类型为 Ev,第二个操作数是一个立即字节,被解释为与 Ev 操作数相同的大小。因此对于83 c3 0101 被解释为 32 位值;对于66 83 c3 01,01 被解释为 16 位值(目标是 16 位 ax 寄存器)。在词干6A 下编码的push 助记符在其单个操作数的大小方面的行为方式相同。

您的问题的更广泛的答案是否定的,没有将 16 位常量解释为 32 位常量的编码。

来源:我写了一个反汇编程序。

【讨论】:

  • 您可能想解释一下Ev 的含义(以防 OP 不知道它表示通用寄存器或有效地址)。
【解决方案2】:

66 81 C3 01 00(在 32 位模式下为“add bx, 01”)可能会被视为一个示例。

没有这样的示例不需要覆盖,因为不需要它。第一个示例需要四个字节的原因是它可以跨越整个 4Gb 范围。第二个只使用一个字节,因为它被限制为 +/-128(总共 256 个值)。通过使用覆盖,我们可以将第一个示例限制为 64kb,但实际上它不是两个字节中的一个字节,它仍然是两个字节。

【讨论】:

  • LEA EBX,[EBX+nn] 可以使用哪些编码?这不会设置标志,所以它不会是一个精确的替换,但对于 128 到 16777215 之间的值,它可能比 ADD 更紧凑。
  • @supercat 可用值的集合是相同的 - 您可以将 32 位立即数用于 4Gb 值,或者将单字节值用于符号扩展值。对于 64kb 值,您还可以使用覆盖 67h 将 [EBX...] 更改为 [BX...]。对于您的 EBX 示例,ADD 和 LEA 的编码大小相同。
  • 我认为该表单具有中等大小。可能不会。不过,我认为 16 位形式根本不等效,因为如果 EBX 是例如0x12345000,BX加上0xBFFF会使EBX等于0x1234FFFF,但是加上0xC000会使EBX等于0x12340000。
  • @supercat 这不是 16 位版本的工作方式。它将对值进行零扩展,因此 ebx=0x12345000 / lea ebx,[bx+1234] -> ebx 0x00006234 因为它正在执行 ebx=bx+1234。
  • 想一想,根据地址、目标操作数或两者是否被修改,实际上不存在三种不同的“16 位”版本吗?无论如何,它们都不会正确处理第 15 位的进位。
【解决方案3】:

在编程中使用小整数是很常见的——不管目标大小。好处是减少了指令编码。许多指令都支持这一点:IMUL、ADD、ADC、SUB、SBB、AND、CMP 等……此外,寻址模式支持字节大小的符号扩展偏移,以帮助减少代码大小。

至于为什么不:与字节编码的节省相比,我认为增加的节省是最小的。 ENTER 指令确实使用 16 位立即数,但它是无符号的并且固定用于更新寄存器 RSP/ESP/SP。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-13
    • 2020-07-13
    • 1970-01-01
    • 2014-11-18
    • 1970-01-01
    相关资源
    最近更新 更多