assembly 是将汇编语句译为机器指令编码,disassembly 是 assembly 的逆操作,将机器指令编码译为汇编语句形式,这里将探讨下 AVX 指令的 assembly 与 disassembly 操作。
AVX 指令可用于 protected mode 和 long mode(包括:64-bit mode 和 compatibility mode),不能使用在 real mode 和 V86 mode,否则会产生 #UD 异常。可是依然能够在 16-bit proected mode 下使用。16-bit protected mode 下它的 default operand-size 和 default address-size 是 16-bit 的。
32-bit 下的 assembly 与 16-bit 下的 assembly,如果指令中含有内存操作数,它们的行为是不同的,其它情况下是一样的。
|
vblendvps xmm1, xmm2, [eax], xmm4
|
这条指令在 32-bit 下译为:
在 16-bit 下仅加上了 address size override prefix:67H
|
67 C4 E3 69 4A 08 40 |
和通用指令的 effective operand size 不同,AVX 指令的 operand size 是固定的,effective operand size 只有一种,依赖于具体的指令。
|
vcvtsi2sd xmm1, xmm2, eax
|
这条指令在 32-bit 和 16-bit 下是一样的,operand3 是通用寄存器 eax 在 16-bit 并不需要加 operand size override prefix,相反加了 66H 会产生 #UD 异常。
|
C5 EB 2A C8 |
表1:xmm/ymm 寄存器
VEX.R |
ModRM.reg |
VEX.vvvv |
寄存器 |
有效 |
VEX.B |
ModRM.r/m |
|||
1 |
000 |
1111 |
xmm0/ymm0 |
16/32/64-bit 有效 |
001 |
1110 |
xmm1/ymm1 |
||
010 |
1101 |
xmm2/ymm2 |
||
011 |
1100 |
xmm3/ymm3 |
||
100 |
1011 |
xmm4/ymm4 |
||
101 |
1010 |
xmm5/ymm5 |
||
110 |
1001 |
xmm6/ymm6 |
||
111 |
1000 |
xmm7/ymm7 |
||
0 |
000 |
0111 |
xmm8/ymm8 |
64-bit 有效 |
001 |
0110 |
xmm9/ymm9 |
||
010 |
0101 |
xmm10/ymm10 |
||
011 |
0100 |
xmm11/ymm11 |
||
100 |
0011 |
xmm12/ymm12 |
||
101 |
0010 |
xmm13/ymm13 |
||
110 |
0001 |
xmm14/ymm14 |
||
111 |
0000 |
xmm15/ymm15 |
从这个表看出,在 16-bit 和 32-bit 只有高半寄存器可以使用, VEX.R 和 VEX.B 必须为 1,这是反相编码设计的结果。
表2:通用寄存器表
VEX.R |
ModRM.reg |
寄存器 |
有效 |
寄存器 |
有效 |
VEX.X |
SIB.index |
||||
VEX.B |
ModRM.r/m,SIB.base |
||||
1 |
000 |
eax |
16/32/64-bit 有效 |
rax |
64-bit 有效 |
001 |
ecx |
rcx |
|||
010 |
edx |
rdx |
|||
011 |
ebx |
rbx |
|||
100 |
esp |
rsp |
|||
101 |
ebp |
rbp |
|||
110 |
esi |
rsi |
|||
111 |
edi |
rdi |
|||
0 |
000 |
r8 |
64-bit 有效 |
||
001 |
r9 |
||||
010 |
r10 |
||||
011 |
r11 |
||||
100 |
r12 |
||||
101 |
r13 |
||||
110 |
r14 |
||||
111 |
r15 |
||||
在 16-bit 和 32-bit 模式下只有 32 位的通用寄存器可用,64 位的寄存器仅在 64-bit 下可用。 VEX.RXB 都须为 1
实际上 64-bit 下的 assembly 与 32-bit 下差不多,不同的 64 位下可以使用全部寄存器,使用 64 位的内存寻址模式。
同样看看这条指令在 64-bit 的情形:
vblendvps xmm1, xmm2, [eax], xmm4
|
结果和在 16-bit 下 assembly 是完全一样的:
67 C4 E3 69 4A 08 40 |
由于在 64-bit 下的 default address-size 是 64 位。因此,对于 32 位的内存寻址同样需要加上 address-size override prefix,那么将上面这条指令形式变得复杂一些:
|
vblendvps ymm1, ymm2, [rax*8 + r10 + 0x11223344], ymm4
|
主要的变化是,寄存器使用了 256 位的 ymm 寄存器,内存寻址模式变复杂了
C4 C3 6D 4A 8C C2 44 33 22 11 40 |
看看这的几个操作数:
ModRM.mod = 10 用来寻址 [SIB+disp32],base 寄存器是 VEX.B + SIB.base = 0010(r10 寄存器),index 寄存器是 VEX.X + SIB.index = 1000(rax 寄存器)。
这里的 VEX.L 为 1 指示处理 256 位的数据。
我们以这个 encodes 来看看 diassembly 逻辑:
|
C4 43 35 4A C2 B0
|
一个纯 binary 的 disassembler 需要为它提供一个提示:dissassemble 为怎样的代码。是 16-bit,还是 32-bit,还是 64-bit
|
int current_bits = get_currnet_bits(); // 得到 bits 指示 if (current_bits == BITS_16) |
首先应该获取字节的 opcode 属性:
|
int encode_attribute = get_encode_attribute(0xC4); if (encode_attribute == OPCODE_ATTRIBUTE) |
在 32-bit 下 C4 字节是 LES 指令的 opcode 码,因此目前认定是 LES 指令,然而接着分析下一个 ModRM 字节。
下一个字节是 43,即 ModRM 字节是 43
|
char ins[64] = "les "; if (MODRM_REG(0x43) == REG_EAX) if (MODRM_RM(0x43) == REG_EBX) case 2: // ModRM.mod = 10b |
ModRM.reg = 000b 那么目标操作数是 eax 寄存器,ModRM.mod 是 01b 那么源操作数是 [register + disp8] 的寻址形式,它需要读取下一个字节 0x35 作为 displacement 字节。而 base 寄存器是 ebx,因此:整条指令就已经完成了:
|
les eax, [ebx+0x35]
|
完成一条指令,接着下一个字节 4A ,这是一条 DEC EDX 指令,无操作数,这样就形成了两条指令:
|
les eax, [ebx+0x35] |
下一个字节是 C2 这是一条 RET 指令,这个 RET 需要 2 个字节的立即数操作数,由于这里只剩下一个字节,因此这个 opcode 是无效的,于是形成:
les eax, [ebx+0x35] |
最后一个字节是 B0,这是一条 MOV 指令的 opcode 码,可是已经是最后一个字节了,这个 opcode 不是单字节的 opcode,因此这个也是无效的,最终结果形成:
les eax, [ebx+0x35] |
C4 字节在 64-bit 下是一个 VEX prefix,是 3-bytes VEX prefix 的首字节,因此接下来分析下一个字节 43 和 35
|
if (encodes[0] == 0xC4) // byte 0 == 0xC4 VEX.R = VEX_R(encodes[1]); VEX.opcode = encodes[3]; |
在这个阶段先分析 VEX prefix 并获取 VEX prefix 的各个域值,接下来字节是 AVX 指令的 opcode 码
在得到了这些信息,包括:VEX prefix 的 byte 0,byte 1 和 byte2,以及 opcode 码,接下来分析这个 VEX prefix 及 opcode 代表什么指令。
根据 VEX.mmmmm,VEX.pp 以及 VEX.opcode 来匹配找到相当的指令
|
get_opcode_attribute(VEX.mmmmm, VEX.pp, VEX.opcode);
|
据此查到 vblendvps 指令,由于 VEX.L = 1,因此指令的操作数是 256 位的。
通过分析 ModRM 字节来得到 operand 的寻址,C2 是 ModRM 字节,从 ModRM 得到:
|
char operand1_id = MAKE_REGID(REX.R, MODRM.reg); |
4 个操作数的 ID 值已经获得,接下来就是生成汇编语句。
|
vblendvps ymm8, ymm9, ymm10, ymm11
|
这是上面的 encodes 反汇编后的结果。