这里,我尝试解析一些实际的指令编码:
(一) 指令Encode 转化为 汇编语句
以下指令编码摘自某句汇编码, 我将这段指令编码,解析成汇编语句:
示例一:
00000016:89 54 24 04
分析如下:
(1) 首先,看看Intel & AMD 的指令编码格式:(基本知识)
+--------+-----------------+-------------+-----------+-------+------------------+----------------+
| Prefix | REX prefix | OpCode | Mod/RM | SIB | Displacement | Immediate |
----------+-----------------+-------------+---------- +-------+-------------------+---------------+
66 40 1 byte 1 byte 1 byte 1,2,4 bytes 1,2,4 bytes
67
2E |
3E |
26
64 althought
65
36 |
F0 |
F3
F2 4F
REX prefix byte:仅仅被支持于:AMD64 处理器,目的是为了扩展64位Operands。
Intel的处理器没有该域。
Mod/RM bytes 由三个域组成: XX XXX XXX ( binary)
—— —— ——
mod reg r/m
SIB bytes 同样由三个域组成:
XX XXX XXX (binary)
—— —— ——
scale index base
(2) 第1个 byte :89 (Opcode),One byte encode。
通过查 Intel 或 AMD 手册得知这个Encode(编码)对应的指令是: Mov Ev, Gv
解释一下:这是一条mov 指令, 有两个Operand(操作数)。
目标Operand是:register 或者 memory operand,大小是 2 bytes 或 4 bytes,具体要根据指令的 default operand size 来定。本例中,这是一个 4 bytes 大小的数。
源Operand 是:register,大小为2 bytes 或 4 bytes。本例中是 4 bytes大小的数。
(3) 第2个byte:54 ( Mod/RM),One byte encode
54 (Mod / RM): 01 010 100
—— —— ——
| | |
+--------|------ --|----------------> mod (模式) : 01
| |
+----------|----------------> register : 010 是 edx 寄存器
|
+---------------> r/m ( memory reference):100
源Operand 是:edx 寄存器。
目标Operand: 在 mod 01 和 r/m 100 配合下,这是一个需要 [SIB] + displacement 的寻址模式,也就是说必须配合 SIB 字节才能正确寻址。故下1 bytes 必定是 SIB byte。
(4) 第3个bytes: 24 (SIB),One byte encode
24 (SIB): 00 100 100
—— —— ——
| | |
+-----------|------------|-------------------> scale: 00
+-----------|--------------------> Index: 100
+-------------------> Base : 100
scale: 00表示 scale 为 1
Index: 100表示 memory operand 的形式为 [base] 的形式
Base: 100表示 base register 为 esp 寄存器
现在,总结分析,目标operand是什么?
Mod/RM 表明:operand有一个 displacement 数
SIB 表明: operand 的形式是 [base]
结合,Mod/RM 与 SIB 得出最终的Operand 寻址模式为:[base + displacement] 的寻址方式。
可得出结果为: [esp + disp]
(5) 最后一个 bytes:04 (displacement)
在SIB byte的下一个字节是 displacement(偏移量),这个偏移量是基于 code segmemt 的。
在这里是04 值。
总结:现在结果可以出来了,汇编形式为:
00000016:89 54 24 04 => movl %edx, 4(%esp)
例子二:
下面摘自某语句的编码,它的汇编形式是什么?
0f b6 4d f8 (hex)
(1) 首先,我们看第1个byte,是:0fh
byte 0F:说明这条指令的Opcode 有两个bytes
并且,这是一条32 bit 的operands 的指令。因为它没有prefix修饰码。
(2) 第2个byte,是:b6h,那么,我们查表(two-byte opcode表)得知:
这条指令是:MOVZX Gv,Eb
说明:这条指是带0扩展传送指令。这条指令的Opcode有2个bytes。
源Operand:Eb说明是1 byte的register或 memory数
目标Operand:Gv是任何一个General purpose register(通用寄存器),其大小是:word(2 bytes),doubleword(4 bytes)或者quadword(8 bytes)
(3) 第3个byte,是:4dh,这个byte是Mod/RM byte。也就是:01 001 101(binary)
得知:目标Operands是:ECX 寄存器。
源Operands的寻址方式是:[ebp] + displacement的形式。
而这个displacement只需要1个bytes,故,下一个byte 必定是displacement
(4) 第4个byte,是:f8h。
基于,第3点所述的,源operand的寻址方式是:[ebp]+displacement,它并不需要SIB byte。
故,这个字节是个displacement。
所以,源operands是:[ebp] + f8。
(5) 总结:结果是:
0f b6 4d f8 (hex) => movzx ecx, byte ptr [ebp + f8] (Intel 格式)
转化我喜欢的AT&T格式为:movzbl 0xf8(%ebp), %ecx
即:movzbl –8(%ebp), %ecx (最终结果)
(二)将汇编语句解析为指令Encode(编码)
如下的语句,是如何转化为Encode的?
一、指令示例1:add %ecx,%eax (AT&A格式)
(1) 查表,ADD Ev, Gv(Intel格式) 的Opcode Encode 是:01
(2) operands encode 由 Mod/RM 决定。
分析如下:Ev 由 Mod/RM bytes中的 mod filed和 r/m filed 来决定
Gv 由 Mod/RM bytes中的 reg filed 来决定。
故:Mod/RM byte 得出结果为:11 001 000(binary) 即:C8(hex)
总结: add %ecx, %eax => 01 C8 (Instruction Encode)
二、指令示例2:add (%esp), %eax (AT&A格式)
(1) 查表得出:ADD Gv, Ev(Intel格式)的Opcode Encode是:03
(2) 同样地,Operands encode由Mod/RM决定,但是,这个例子中,源操作数的寻址方址是SIB形式,即:disp(base,index,scale) 的形式。故,光有Mod/RM byte是不够的,还必须结合SIB byte才行。
故,初步确定,这条指令需要3个bytes。包括:Opcode encode 、Mod/RM encode 以及 SIB encode。
(3) GV 由 Mod/RM 中的 reg 域来决定。同时,Mod/RM 中的 r/m 域决定SIB 寻址。
故: Mod/RM 结果为:00 000 100 (binary),即:04(hex)
(4) Ev 由最终由SIB来决定。SIB中的base 域决定哪个 base register。
SIB中的 Index 域决定寻址方式。
故:SIB 结果为: 00 100 100 (binary),即:24(hex)
(5) 最终结果编码为:
add (%esp),%eax => 03 04 24 (Instruction Encode)
三、指令示例3:
这里,我将AMD64的指令转化为Encodes
指令为:mov rax, [rbx](Intel格式,64位的操作数)
REX prefix 编码为:
0100 X X X X
—— — — — —
4 W R X B 位
(1) AMD 的REX prefix 设定了64-bit operands,故这条指令,必定需要REX prefix
REX的W位(第3 bit)作用是扩展64位操作数,此位置为1时,可以使用AMD的64-bit operand size。这里为1。
REX的B位(第0 bit)作用是扩展64寄存器。这里为1。
REX的R位(第2 bit)作用是扩展64寄存器。这里为1。
REX的X位(第1 bit)作用在SIB上,用于扩展寻址寄存器。这里为0。
故整个REX prefix 为:4D
(3) 查表得:MOV Gv, Ev 编码为:8B
(2) Mod/RM byte为:03。这里并不需要SIB寻址方式
若:源操作数是[rbx + rcx]的情况下,就需要SIB字节了!因为,有了一个SIB寻址方式之故。
(3) 最终结果编码为:
mov rax,[rbx] => 4D 8B 03 (hex)
注意:这条指令必须是AMD64处理器,在被初始化64位模式下才能执行。
若:4D 8B 03 在IA32下执行会是:意想不到的结果!
希望我的分析能带给你一些帮助。当然,这里不可能很详细的讲解,其中Encode(编码)请查Intel 和 AMD 的手册,一些架构性的知识,也可以从指令中获得。
例如:
1、AMD为了支持64技术,在指令结构中引进了REX Prefixs。增加了8个通用寄存器(r8 – r15)。8个调试寄存器,8个控制寄存器。更有效地址的寻址方式
2、指令的default operad size 是32位的,在64-bit 模式中,AMD的REX Prefix 中的W(bit)扩充为64-bit。而 Prefix 66h 可以更改 Operand size 大小。Default address operands size 是64位的。Prefix 67h 可以更改Address operand size 的大小。
3、指令的缺省size由 CPU 当前的模式决定,若CPU 被初始化为64-bit 模式,则可以利用AMD64 技术。
4、通过,对指令编码的学习,可以开发自己的汇编语言编译器,从而最终实现自己的后端汇编器。实现64编译器。这就是我的目的!