x86 32 位操作码在 x86-x64 中不同或完全删除

x86 32 bit opcodes that differ in x86-x64 or entirely removed

我在维基百科上查找了 x86-x64 中的 x86 向后兼容性,它说:

x86-64 is fully backwards compatible with 16-bit and 32-bit x86 code.Because the full x86 16-bit and 32-bit instruction sets remain implemented in hardware without any intervening emulation, existing x86 executables run with no compatibility or performance penalties,whereas existing applications that are recoded to take advantage of new features of the processor design may achieve performance improvements.

所以我测试了一些指令,发现有些指令实际上会产生完全不同的操作码(而不仅仅是应用前缀),例如:INC/DEC。 查看 (x86):

\x40 inc eax
\x48 dec eax

并且在 x86-x64 中组装时产生:

\xff \xc0 inc eax

\xff \xc8 dec eax

我正在尝试找出具有相同症状但会产生不同操作码的其他指令的原因和更多示例。 我很熟悉 push、pop、call、ret、enter 和 leave 在 x86-x64 中不可用 32 位。

几乎所有在两种模式下可用的指令在两种模式下都具有相同的操作码。

删除说明:

  • 二进制编码的十进制东西,例如 AAM(乘法后的 ASCII 调整),用于在一个包含两个 base-10 数字的寄存器上执行正常二进制 add/sub/mul/div 后修复二进制编码的十进制在每个 4 位半中。反正他们 运行 慢慢来,没用过。将数字存储为二进制整数而不是 BCD 很普遍。
  • 删除了 CS/DS/ES/SS 中的
  • push / pop。 push/pop FS 和 GS 仍然有效(这两个段在长模式下仍然可以有一个非零基数)。 mov Sreg, r32mov r32, Sreg 仍然可用于“绝育”段寄存器,因此您可以使用临时整数 reg 模拟 push/pop。 CS 仍然很重要;远跳到另一个代码段可以切换到 32 位模式,其他的仍然需要有效的段描述符。
  • 其他晦涩的段内容,如 ARPL:调整段选择器的 RPL 字段。它实际上只是整数寄存器的位域钳位和设置标志指令,因此可以在内核可能需要它的极少数地方由其他一些指令模拟。
  • 也许是编译器从未在 32 位代码中使用的其他一些晦涩或特权指令。 (并不是编译器曾经发出过上述任何一种,没有内在函数或内联汇编。)

删除(改变用途)编码一些仍然可用的指令:在你的情况下,32 位可以使用 inc r32 单字节操作码(0x40 + 寄存器号)。 64 位模式只有 inc r/m32 编码,其中要递增的寄存器用第二个字节指定。 (在这种情况下,0x4x 字节被重新用作 REX 前缀字节。)

Intel的insn参考(按照the x86 tag wiki) shows the following for inc中的link:

Opcode   Instruction Op/   64-Bit   Compat/
                     En     Mode    Leg mode

FF /0   INC r/m32     M     Valid     Valid     Increment r/m doubleword by 1.
40+ rd  INC r32       O      N.E.     Valid     Increment doubleword register by 1.

N.E。表示不可编码。 Op/En 列描述了 ope运行ds 是如何编码的。


Jan Hubicka 的 AMD64 ISA overview 简要描述了 REX 前缀的单字节 inc/dec 操作码的再利用,以及默认的 ope运行d 大小以及即时数据如何仍然是 32 位. movabs 可用于加载 64 位立即数,或 load/store from/to 64 位绝对地址。

AMD's AMD64 manual第 2.5.11 节重新分配的操作码 有一个非常短的 table。它只列出:

  • 4x inc/dec r32 变成了 REX 前缀
  • 63 ARPL 变为 MOVSXD(当与 REX.W=1 一起使用时,将 dword 符号扩展为 qword(这意味着 REX 前缀中的 W 位 = 1))。

早期的 AMD64 和 Intel EM64T CPU 在长模式中遗漏了 SAHF/LAHF,但后来重新添加了该指令,其操作码与 32 位相同。 table 也没有列出完全删除的指令(BCD 指令和其他指令)以便为将来可能的扩展腾出空间。


他们本可以简化很多事情,并使 x86-64 成为一个更好更清晰的指令集,为未来的扩展留出更多空间,但是 与 32 位的每一个区别都意味着更多的解码器 t运行姐妹。在 64 位中没有移动到不同操作码的机器指令。

多条机器指令通常共享相同的 asm 助记符,mov 是重载最多的指令。有加载、存储、带立即数的 mov、移动 to/from 段寄存器,都是 8 位和 32 位的。 (16 位是带有 ope运行d-size 前缀的 32 位,与带有 REX 前缀的 64 位相同。)有一个特殊的操作码用于从 64 位绝对地址加载 RAX。还有一个特殊的操作码,用于将 64 位立即常量加载到寄存器中。 (AT&T 语法称之为 movabs,但它在 Intel/NASM 中仍然只是 mov

该说法是正确的,但有些误导。 x86-64架构向后兼容x86,但32位指令集不兼容64位指令集

您可以使用兼容模式 运行 在 x86-64 CPU 上编写 x86 代码。实际上,由于 CPU 应该是透明的 x86 到 x86 代码,它恰恰相反:当你想要 运行 x86-64 代码时,你进入 64 位模式(长模式)。这意味着您不能同时 运行,但可以从一种模式切换到另一种模式。