X64(64位)汇编指令与机器码转换原理
- 1 64位寻址形式下的ModR/M字节
- 1.1 寻址方式
- 1.2 寄存器编号
- 2 汇编指令转机器码
- 2.1 mov rcx, 1122334455667788h
- 2.2 mov rcx,[r8]与mov [r8],rcx
- 2.3 mov rcx,[r8+r9*2]
本文属于《 X86指令基础系列教程》之一,欢迎查看其它文章。
1 64位寻址形式下的ModR/M字节
x64指令机器码组成:
REX prefix组成:
ModR/M组成:
1.1 寻址方式
ModR/M字节具体值,组成情况,如下图所示:
ModRM.mod、ModRM.r/m和REX.B三者确定一种内存寻址方式,一共有64种内存寻址方式。
1.2 寄存器编号
ModRM.reg与REX.R位,合并扩展为4位Rrrr,表示X64架构下这16个寄存器编号,具体编号如下:
r8(/r) r16(/r) r32(/r) r64(/r) mm(/r) | AL AX EAX RAX MM0 | CL CX ECX RCX MM1 | DL DX EDX RDX MM2 | BL BX EBX RBX MM3 | AH SP ESP RSP MM4 | CH BP EBP RBP MM5 | DH SI ESI RSI MM6 | BH DI EDI RDI MM7 | r8b r8w r8d r8 | r9b r9w r9d r9 | r10b r10w r10d r10 | r11b r11w r11d r11 | r12b r12w r12d r12 | r13b r13w r13d r13 | r14b r14w r14d r14 | r15b r15w r15d r15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Rrrr | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
2 汇编指令转机器码
我们举几个立即数操作的例子:
mov cl, 12h
mov cx, 1234h
mov ecx, 12345678h
mov rcx, 1122334455667788h
然后,把表格中每一类寻址方式,都举一个例子,进行描述,如下:
序号 | 寻址方式 | Mod | R/M | REX.B | 汇编例子 | 含义 |
---|---|---|---|---|---|---|
1 | [r8] | 00 | 000 | 1 | mov rcx,[r8]与mov [r8],rcx | (rcx)=((r8))与((r8))=(rcx) |
2 | [SIB] | 00 | 100 | 任意 | mov rcx,[r8+r9*2] | (rcx)=((r8)+(r9)*2) |
3 | [disp32] / [rip+disp32] | 00 | 101 | 任意 | mov rcx,[00001000h] | (rcx)=(00001000h) / (rcx)=((rip)+00001000h) |
4 | [r8+disp8] | 01 | 000 | 1 | mov rcx,[r8+10h] | (rcx)=((r8)+10h) |
5 | [SIB+disp8] | 01 | 100 | 任意 | mov rcx,[r8+r9*2+10h] | (rcx)=((r8)+(r9)*2+10h) |
6 | [r8+disp32] | 10 | 000 | 1 | mov rcx,[r8+00001000h] | (rcx)=((r8)+00001000h) |
7 | [SIB+disp32] | 10 | 100 | 任意 | mov rcx,[r8+r9*2+00001000h] | (rcx)=((r8)+(r9)*2+00001000h) |
8 | r8 | 11 | 000 | 1 | mov r9,r8 | (r9)=(r8) |
9 | r9 | 11 | 001 | 1 | mov r8,r9 | (r8)=(r9) |
因此,一共有13个汇编指令转机器码的例子,接下来,依次来讲解。
2.1 mov rcx, 1122334455667788h
- mov cl, 12h
mov cl, 12h
表示将立即数12h存储到8位
寄存器cl中。
查询指令手册,与MOV r8,imm8
指令相符,其操作码为B0+ rb,rb表示目的操作数cl寄存器编号。
当前指令cl编号为1,因此操作码为B0+1=B1。
因此指令的机器码组成,如下所示:
汇编指令 | 操作码 | 立即数 | 机器码 |
---|---|---|---|
mov cl, 12h | B1 | 12 | B112 |
- mov cx, 1234h
mov cx, 1234h
表示将立即数1234h存储到16位
寄存器cx中。
查询指令手册,与MOV r16,imm16
指令相符,其操作码为B8+ rw,rw表示目的操作数cx寄存器编号。
当前指令cx编号为1,因此操作码为B8+1=B9。
由于在X64下,默认操作数宽度是32位,需要添加指令前缀66H,“反转”选择16位宽度的操作数。
因此指令的机器码组成,如下所示:
汇编指令 | 指令前缀 | 操作码 | 立即数 | 机器码 |
---|---|---|---|---|
mov cx, 1234h | 66 | B9 | 1234 | 66B93412 |
- mov ecx, 12345678h
mov ecx, 12345678h
表示将立即数12345678h存储到32位
寄存器ecx中。
查询指令手册,与MOV r32,imm32
指令相符,其操作码为B8+ rd,rd表示目的操作数ecx寄存器编号。
当前指令ecx编号为1,因此操作码为B8+1=B9。
由于在X64下,默认操作数宽度是32位,刚好合适,因此不需添加指令前缀66H。
因此指令的机器码组成,如下所示:
汇编指令 | 操作码 | 立即数 | 机器码 |
---|---|---|---|
mov ecx, 12345678h | B9 | 12345678 | B978563412 |
- mov rcx, 1122334455667788h
mov rcx, 1122334455667788h
表示将立即数1122334455667788h存储到64位
寄存器rcx中。
查询指令手册,与MOV r64,imm64
指令相符,其操作码为REX.W + B8+ rd,rd表示目的操作数rcx寄存器编号。
当前指令rcx编号为1,因此操作码为B8+1=B9。
REX.W说明需要REX前缀,REX.W=1表示操作位宽为64位,因此REX=48h。
因此指令的机器码组成,如下所示:
汇编指令 | REX前缀 | 操作码 | 立即数 | 机器码 |
---|---|---|---|---|
mov rcx, 1122334455667788h | 48 | B9 | 1122334455667788h | 48 B9 88 77 66 55 44 33 22 11 |
2.2 mov rcx,[r8]与mov [r8],rcx
- mov rcx,[r8]
mov rcx,[r8]
表示将r8寄存器中地址指向的内存单元,存储到rcx中。
查询指令手册,与MOV r64,r/m64
指令相符,其操作码为REX.W + 8B /r,/r表示这条指令具有ModR/M字段。
因此,尝试推导ModR/M值。当前指令寻址方式属于《X64指令基本格式》中“第一种,无SIB字节的内存寻址”,ModRM.mod、ModRM.r/m、REX.B三者确定一种内存寻址方式,如下图:
第一步,在源和目的操作数中,以内存寻址操作数为坐标点,反推ModRM.mod、ModRM.r/m、REX.B。
这里内存寻址操作数为[r8],故ModRM.mod=00,ModRM.r/m=000,REX.B=1。
第二步,在源和目的操作数中,以寄存器操作数,反推ModRM.reg与REX.R。
这里寄存器操作数为rcx,其寄存器编号Rrrr=0001,故REX.R=0,ModRM.reg=001。
REX.W说明需要REX前缀,REX格式为0100WRXB,W表示操作数宽度,W=1表示64位,X位默认为0,因此REX=01001001=49h。ModRM=00001000=08h。
因此指令的机器码组成,如下所示:
汇编指令 | REX前缀 | 操作码 | ModRM | 机器码 |
---|---|---|---|---|
mov rcx,[r8] | 49 | 8B | 08 | 498B08 |
- mov [r8],rcx
mov [r8],rcx
表示将rcx寄存器内容,存储到r8寄存器中地址指向的内存单元中。
查询指令手册,与MOV r/m64,r64
指令相符,其操作码为REX.W + 89 /r,/r表示这条指令具有ModR/M字段。
因此,尝试推导ModR/M值。当前指令寻址方式属于《X64指令基本格式》中“第一种,无SIB字节的内存寻址”,ModRM.mod、ModRM.r/m、REX.B三者确定一种内存寻址方式,如下图:
第一步,在源和目的操作数中,以内存寻址操作数为坐标点,反推ModRM.mod、ModRM.r/m、REX.B。
这里内存寻址操作数为[r8],故ModRM.mod=00,ModRM.r/m=000,REX.B=1。
第二步,在源和目的操作数中,以寄存器操作数,反推ModRM.reg与REX.R。
这里寄存器操作数为rcx,其寄存器编号Rrrr=0001,故REX.R=0,ModRM.reg=001。
REX.W说明需要REX前缀,REX格式为0100WRXB,W表示操作数宽度,W=1表示64位,X位默认为0,因此REX=01001001=49h。ModRM=00001000=08h。
因此指令的机器码组成,如下所示:
汇编指令 | REX前缀 | 操作码 | ModRM | 机器码 |
---|---|---|---|---|
mov [r8],rcx | 49 | 89 | 08 | 498908 |
发现没有,源操作数与目的操作数,交换传输方向后,仅操作码发生变化。也就是说,寄存器与内存之间传递数据的方向,是靠操作码来分辨的,与REX前缀、ModRM无关。
2.3 mov rcx,[r8+r9*2]
mov rcx,[r8+r9*2]
表示将r8寄存器中地址+r9寄存器中地址*2,指向的内存单元,存储到rcx中。
查询指令手册,与MOV r64,r/m64
指令相符,其操作码为REX.W + 8B /r,/r表示这条指令具有ModR/M字段。
因此,尝试推导ModR/M值。当前指令寻址方式属于《X64指令基本格式》中“第三种,带SIB字节的内存寻址”,如下图所示:
由于此场景下REX.B与SIB.base合并,不再与ModRM.r/m合并。
因此ModRM.mod、ModRM.r/m两者就可以确定[SIB]内存寻址方式,如下图:
第一步,以内存寻址操作数为坐标点,反推ModRM.mod、ModRM.r/m。
这里内存寻址操作数为[r8+r9 * 2],故ModRM.mod=00,ModRM.r/m=100。
第二步,以寄存器操作数,反推ModRM.reg与REX.R。
这里寄存器操作数为rcx,其寄存器编号Rrrr=0001,故REX.R=0,ModRM.reg=001。
第三步,。。。。。。
REX.W说明需要REX前缀,REX格式为0100WRXB,W表示操作数宽度,W=1表示64位,X位默认为0,因此REX=01001001=49h。ModRM=00001000=08h。
因此指令的机器码组成,如下所示:
汇编指令 | REX前缀 | 操作码 | ModRM | 机器码 |
---|---|---|---|---|
mov [r8],rcx | 49 | 89 | 08 | 4B 8B 0C 48 |
发现没有,源操作数与目的操作数,交换传输方向后,仅操作码发生变化。也就是说,寄存器与内存之间传递数据的方向,是靠操作码来分辨的,与REX前缀、ModRM无关
。
未完待续。。。。