x86指令集 字节大小
https://bbs.kanxue.com/thread-190127.htm
最近对x86_32架构下的许多程序进行了指令长度统计,结果表明所有程序所涉及的指令长度范围均为:1~11字节。
而根据INTEL 开发者手册上介绍的指令的最大长度限制为15字节。
但是,在什么情况或者架构上才会有12~15字节长度的指令呢?
还有,是否可以断定在X86_32架构下指令的最大长度为:11字节?如果是,有木有计算依据?
lock add dword ptr es:[eax+ecx*8+0x11223344], 0x12345678
在16 位模式下是15 bytes:
26 66 67 F0 81 84 C8 44 33 22 11 78 56 34 12
lock add dword ptr es:[eax+ecx*8+0x11223344], 0x12345678
仅仅在 16 位模式下,这条汇编语句的 encode 是 15 bytes
因为:它在 16 位模式下,需要进行 operand size override 和 address size override,因此能达到 prefix 的饱和
从而每个部分除了 opcode 外,都达到了饱和状态。刚刚好是 15 bytes:26 66 67 F0 81 84 C8 44 33 22 11 78 56 34 12
4 group 的 prefix 都使用上了,ModRM 和 SIB 都需要,displacement 和 immediate 都是 4 bytes 的,只有 opocde 是 1 byte
这条指令在 32 位是:26 F0 81 84 C8 44 33 22 11 78 56 34 12 (13 bytes)
在 64 位下:26 67 F0 81 84 C8 44 33 22 11 78 56 34 12 (14 bytes)
那是因为 32 位下缺省的 operands size 和 address size 是 32 位,不需要作 operand size override 和 address size override
在 64 位下缺省的 operands size 是 32 位,而 address size 是 64 位,因此不需要作 operand size override,但是需要做 address size override
disassembler 的反汇编的指令边界与真实执行指令边界可能会不一致。当然造成这种不一样的人为的,实际上这是对 disassembler 的一种考验。
不要期望编译器会产生大于 15 bytes 的指令编码!除非它有错。不要认为 disassmbler(反汇编器)的结果就是真实执行指令结果,有可能会不一致!
现在:我们可以回答前面提到的三个疑问:
- 真实的指令长度确实为 15 bytes
- processor 会给我们保证这一点
- 当加载超过 15 bytes 长度的指令时,processor 会给我们抛出 #GP 异常,它通过这种方式保证了 15 bytes 指令长度
x86指令集 :
16位,最大长度15字节
32位,最大长度13字节
64位,最大长度14字节
INSTRUCTION PREFIXES (前缀只有固定的这几个)
The instruction prefixes are divided into four groups, each with a set of allowable prefix codes:
• Lock and repeat prefixes.
— F0H—LOCK prefix.
— F2H—REPNE/REPNZ prefix (used only with string instructions).
— F3H—REP prefix (used only with string instructions).
— F3H—REPE/REPZ prefix (used only with string instructions).
• Segment override.
— 2EH—CS segment override prefix.
— 36H—SS segment override prefix
— 3EH—DS segment override prefix.
— 26H—ES segment override prefix.
— 64H—FS segment override prefix.
— 65H—GS segment override prefix.
• Operand-size override, 66H
• Address-size override, 67H
For each instruction, one prefix may be used from each of these groups and be placed in any
order. The effect of redundant prefixes (more than one prefix from a group) is undefined and may
vary from processor to processor.
32位
例2:随便找一个机器码如:FF 15 D4 81 DF 00
那么它的汇编语句是什么呢?
解码器会依次读入第 1 个字节,第 2 个字节等 ... 进行判断:
(1)第 1 个字节是什么? prefix 还是 opcode
(2)同样再判断第 2 个字节是不是 prefix,如果不是 prefix ,那么它就是 opcode 码
(3)如果第 2 个字节是 opcode 码,再判这个 opcode 需不需 ModRM 字节,如果需要,第 3 个字节就是 ModRM 字节
(4)根据 ModRM 字节判断需不需要 SIB 字节,第 4 个字节如果需它就是 SIB 字节
(5)判断 ModRM 字节是否需要 displacment 字节和 immediate 字节
根据上面的逻辑,得出:
FF:这个字节是个具有 Group 属性的 Opcode 码,它进行什么操作需要依赖于 ModRM 字节的 Reg 域。
换句话来说,FF 并不是完整独立的 Opcode 码,它要联合 ModRM 才能确定具体的操作。
15:这个是 ModRM 字节(mod-reg-r/m): ModRM.mod = 00,ModRM.reg = 010,ModRM.r/m = 101。
其中 ModRM.reg 域被 FF(opcode) 作为确定具体操作码的参考。
● Opcode + ModRm.reg : FF /010 最终确定为:Call 指令。
● ModRM.mod:00 表示操作数是 memory
● ModRM.r/m:这是一个 32 位的 displacement 值。
所以,这个机器码最终被解码为: call dword ptr [00DF81D4]
下面的直观的图表
下面来看看 x86/x64 指令 encode 的 2 个例子:
mov word ptr es:[eax + ecx * 8 + 0x11223344], 0x12345678
例子1:在当前 32 位系统下,有下面汇编语句(Intel 格式):
这是一条** mov** 指令,目标操作数是** memory**,源操作数是 immediate
注意:
我特地将目的操作数的大小定为是 word(2个字节),而不是 dword。但是,源操作数却是一个 dword 大小的 0x12345678。
那么:对于这条汇编语句,编译器应该如何处理呢?
- 应该怎样处理 immediate 部分
- 应该选择哪条 mov 指令
- operand size 是多少
- 在 32 位环境下,如何生成 16 位 operand 或 address 的代码
- 寻址模式是什么
这些问题是我们最终需要掌握的知识,这是本栏目的最终目的。
实际上,它最终形成的机器编码是:26 66 c7 84 c8 44 33 22 11 78 56
(1)dword 大小的源操作数,会被编译器截断为 word 大小
(2)选择 MOV Ev, Iz 指令,它的 opcode 是 C7
(3)它的 operand size 是 word 大小,取决于 first operand 的 size
(4)在 32 位下,通过使用 operand size override 可以造型为 16 位 operands size,使用 address size override 可以造型为 16 位 address size
(5)它的寻址模式是:基址+变址寻址,它需要提供 SIB 字节
下面的图直观的分解这条指令的组成部分:
26:在指令序列里是:prefix 部分,它是 semgent overrride prefix ,作用是调整内存操作数的段选择子
66:在指令序列里是:prefix 部分,它是 operand size override prefix ,作用是调整操作数的缺省大小
C7:在指令序列里是:Opcode 部分,是 mov 指令是操作码
84:在指令序列里是:ModRM 值,定义操作数的属性
C8:在指令序列里:SIB 值定义内存操作数的属性
44332211:在指令序列里是: displacement 值
7856:在指令序列里是:immediate 值
这 2 个例子,作为对学习 x86/x64 指令编码的一个感性认识。
======== End