汇编:为什么一些 x86 操作码在 x64 中无效?
Assembly: why some x86 opcodes are invalid in x64?
为什么在 x64 中一些操作码无效(例如 06
、07
),而在 x86 中用于相当基本的指令(06
和 07
是 push
和 pop
)?我认为那些最简单的指令在两种架构中都能很好地发挥作用。
为什么他们在 x64 中禁用了一些简单的指令?他们为什么不工作?
为什么他们禁用一些操作码,在操作码列表中创建漏洞,而他们可以将它们分配给 x64 版本的指令?
参考:
32位模式下的06
和07
操作码是指令PUSH ES
和POP ES
。在 64 位模式下,段寄存器 CS、DS、ES 和 SS 不再用于确定内存地址:处理器假定基地址为 0 并且没有大小限制。由于现在通常没有理由让应用程序(操作系统本身除外)访问这些寄存器,因此删除了用于更改和访问它们的 push/pop 操作码,只留下 mov
to/from Sreg(这只是总共 2 个操作码;寄存器号进入 ModRM 字节而不是 1 字节操作码的一部分)。对于几乎不需要的东西,这已经足够了。
FS和GS段寄存器在64位模式下仍然可以设置基地址,所以与其相关的push和pop操作码没有被移除。 (这些 2 字节 0F xx
操作码是在 386 中添加的,与 8086 段寄存器的旧 1 字节操作码相比,它们是操作码 space 中价值较低的部分。
Push/pop 或 mov
段寄存器不是操作系统通常设置 FS 或 GS 段基数的方式,但是:这需要 GDT 或 LDT 条目,并且只能在其中设置一个基数低 32 位。 64 位操作系统将使用关联的 MSR 直接读写基址,而不是架构寄存器。 (现代 32 位操作系统也这样做,除非 运行 在不支持基于段的 MSR 的旧硬件上。)
对于所有 CPU 来说,都有一个类似 "opcode space" 的东西。例如,如果 CPU 使用 8 位操作码,那么会有一个最大值。它可能有 256 条指令。操作码越大,您可以拥有的操作码越多,但越难快速获取和解码它们。
80x86是比较老的架构。它从一个适度的操作码 space 开始,主要由 1 字节和 2 字节的操作码组成。每次 CPU 制造商添加一项新功能时,它都会从操作码 space 中获取更多操作码。他们 运行 超出了操作码。他们 运行 很快就出来了。
为了解决这个问题,他们开始做一些事情,比如添加转义码和前缀来人为地扩展操作码 space。例如,对于最近的 AVX 指令,您正在查看 VEX 前缀,后跟 old/recycled 转义码(例如 0xF0),然后是 old/recycled address/operand 大小前缀(例如 0x66) , 接着是另外 4 个字节。不好看。
同时,还有现在很少使用的旧指令(AAD、AAM 等)和具有 multiple/redundant 操作码 (INC/DEC) 的指令正在消耗宝贵的“1 字节”操作码.由于向后兼容性,这些无法t/can完全删除。
但是;当设计 64 位时,根本没有任何 64 位代码可以兼容——向后兼容性并不重要。 "not very important" 指令使用的 1 字节操作码可以回收;使这些指令在 64 位代码中无效(但释放了一些有价值的 1 字节操作码)。
大多数 1 字节操作码(如果我没记错的话,整个 1 字节 INC/DEC 组)立即被回收用于支持 64 位操作所需的 REX 前缀运行 DS。有些不是,而是变成了 "free for future extensions"(限制是扩展只能在 64 位代码中工作,因为这些指令在 16 位和 32 位代码中仍然有效)。
为什么在 x64 中一些操作码无效(例如 06
、07
),而在 x86 中用于相当基本的指令(06
和 07
是 push
和 pop
)?我认为那些最简单的指令在两种架构中都能很好地发挥作用。
为什么他们在 x64 中禁用了一些简单的指令?他们为什么不工作? 为什么他们禁用一些操作码,在操作码列表中创建漏洞,而他们可以将它们分配给 x64 版本的指令?
参考:
32位模式下的06
和07
操作码是指令PUSH ES
和POP ES
。在 64 位模式下,段寄存器 CS、DS、ES 和 SS 不再用于确定内存地址:处理器假定基地址为 0 并且没有大小限制。由于现在通常没有理由让应用程序(操作系统本身除外)访问这些寄存器,因此删除了用于更改和访问它们的 push/pop 操作码,只留下 mov
to/from Sreg(这只是总共 2 个操作码;寄存器号进入 ModRM 字节而不是 1 字节操作码的一部分)。对于几乎不需要的东西,这已经足够了。
FS和GS段寄存器在64位模式下仍然可以设置基地址,所以与其相关的push和pop操作码没有被移除。 (这些 2 字节 0F xx
操作码是在 386 中添加的,与 8086 段寄存器的旧 1 字节操作码相比,它们是操作码 space 中价值较低的部分。
Push/pop 或 mov
段寄存器不是操作系统通常设置 FS 或 GS 段基数的方式,但是:这需要 GDT 或 LDT 条目,并且只能在其中设置一个基数低 32 位。 64 位操作系统将使用关联的 MSR 直接读写基址,而不是架构寄存器。 (现代 32 位操作系统也这样做,除非 运行 在不支持基于段的 MSR 的旧硬件上。)
对于所有 CPU 来说,都有一个类似 "opcode space" 的东西。例如,如果 CPU 使用 8 位操作码,那么会有一个最大值。它可能有 256 条指令。操作码越大,您可以拥有的操作码越多,但越难快速获取和解码它们。
80x86是比较老的架构。它从一个适度的操作码 space 开始,主要由 1 字节和 2 字节的操作码组成。每次 CPU 制造商添加一项新功能时,它都会从操作码 space 中获取更多操作码。他们 运行 超出了操作码。他们 运行 很快就出来了。
为了解决这个问题,他们开始做一些事情,比如添加转义码和前缀来人为地扩展操作码 space。例如,对于最近的 AVX 指令,您正在查看 VEX 前缀,后跟 old/recycled 转义码(例如 0xF0),然后是 old/recycled address/operand 大小前缀(例如 0x66) , 接着是另外 4 个字节。不好看。
同时,还有现在很少使用的旧指令(AAD、AAM 等)和具有 multiple/redundant 操作码 (INC/DEC) 的指令正在消耗宝贵的“1 字节”操作码.由于向后兼容性,这些无法t/can完全删除。
但是;当设计 64 位时,根本没有任何 64 位代码可以兼容——向后兼容性并不重要。 "not very important" 指令使用的 1 字节操作码可以回收;使这些指令在 64 位代码中无效(但释放了一些有价值的 1 字节操作码)。
大多数 1 字节操作码(如果我没记错的话,整个 1 字节 INC/DEC 组)立即被回收用于支持 64 位操作所需的 REX 前缀运行 DS。有些不是,而是变成了 "free for future extensions"(限制是扩展只能在 64 位代码中工作,因为这些指令在 16 位和 32 位代码中仍然有效)。