x86 子指令操作码混淆

x86 sub instruction opcode confusion

玩了一下 Turbo 汇编器和 Turbo 调试器,我对操作码感到惊讶。更准确地说,我有一些汇编二进制文件,其中 Turbo Debugger 反汇编了单词

29 C3

正确为 sub bx, ax。但是,Turbo 汇编程序将完全相同的指令 sub bx, ax 汇编为以下单词

2B D8

对此感到困惑,我发现 this 参考文献指出从一个寄存器中减去一个寄存器可能确实以 292B 开始。 完全同一条指令真的可以用不同的操作码表达吗?如果是这样,那是为什么?是出于历史原因和兼容性吗?参考说明了操作码的不同操作数类型,它们恰好与 sub bx, ax 一致。这是为了以后能够通过自修改代码等在不同的操作数中打补丁吗?此外,Turbo Assembler 是否有语法结构来选择一个操作码而不是另一个?

注意: 我知道像 jejz 这样的条件跳转具有相同的操作码,因为它们具有相同的标志相关行为和不同的助记符反映了同一个操作的不同语义,但前者让我很困惑。

大多数x86指令支持两个操作数,其中一个操作数可以是内存操作数。这通过在 modr/m 字节中编码操作数来支持。这个字节总是编码一个寄存器操作数和一个寄存器或内存(r/m)操作数,但是指令必须决定它的哪个操作数是寄存器操作数,哪个是内存操作数。

因此,为了支持在源操作数或目标操作数中包含内存操作数,许多指令都具有一种变体,其中源操作数可以是内存操作数,另一种变体是目标可以是内存操作数。这通常由位 01 控制(在某些手册中称为 d 位)。

因此,不需要内存操作数的指令可以采用两种方式进行编码,并且汇编器通常会选择一种或另一种作为某种程度上随机的实现细节。