X64指令基本格式
- 1 REX Prefix结构
- 2 REX prefix扩展位
- 2.1 第一种,无SIB字节的内存寻址(mod !=11 )
- 2.2 第二种,寄存器到寄存器的寻址(无内存操作数,mod=11)
- 2.3 第三种,带SIB字节的内存寻址(mod!=11 & r/m=100)
- 2.4 第四种,寄存器操作数编码于操作码字节中
- 3 RIP寻址
本文属于《 X86指令基础系列教程》之一,欢迎查看其它文章。
X64指令与X86指令兼容,因此不再对X86指令结构重复描述,主要以讲解X64上新特性为主。
1 REX Prefix结构
x64指令机器码,在原来X86 32位基础上,增加了Rex Prefix,如下所示:
AMD推出 x86 扩展 64 位技术时,增加了一个用于访问扩展的 64 位数据 prefix,它是:REX prefix,而 x86 原有的 prefix 则变为了指令格式中的:Legacy prefix。
REX prefix 仅存在于 x64 的 64-bit 模式中,在 legacy x86 模式下,REX prefix 是无效的。 Legacy prefix在 x86 和x64下,均是有效的。
REX prefix 的取值范围是:40H - 4FH。在非 64 位模式下,它们是 inc 与 dec 指令;也就是说这些指令在 64 位模式下被重定义为 REX prefix,关于Prefix的介绍真的是少之又少,不过可以在x86/x64 指令编码内幕(适用于 AMD/Intel)学到很多。
这里就简单说一下吧,一个Legacy prefix还有一个REX prefix。这些修饰符是为了改变缺省的寄存器,比如说在32位下mov ax,bx。那么就要有Legacy prefix 0x66修饰,前者还有个作用就是改变当前段寄存器,不多这在目前已经显得不怎么重要了。
到了x64的时候,由于通用寄存器的扩展(主要原因),原本的8个通用寄存器只要3个位来标识,但是现在多了r8~r15,16个通用寄存器怎么办呢,很明显加上一位就能完全标识了。
那么就是REX prefix的作用,看一下这个家伙的结构:
各个位的含义,如下:
他有取值范围,前4位一定是4,后四位可选,W,R,X,B。
- W位,标识改变默认操作数大小,比如现在x64有个汇编代码mov r8,r10。一般很多指令都是默认32位操作数的,只有在CS.L1&&CS.D0的时候才会是64位操作数(我没见过)。
所以一般都要去改变默认操作数大小,那REX prefix就是0x48 (Inter手册上常说是 REX.W); - R位,用来扩展 ModRM.reg 域,000 ~ 111 —> 0 000 ~ 1 111 ,于是现在寄存器的情况变成了如下:
- X位,用来扩展 SIB.index 域;
- B位,用来扩展 SIB.base, ModRM.r/m 以及 Opcode.reg。
2 REX prefix扩展位
REX prefix=0100WRXB,扩展位WRXB的使用场景,有以下4种。
2.1 第一种,无SIB字节的内存寻址(mod !=11 )
比如:mov rcx,[r8]
,机器码:49 8B 0B
- 扩展ModRM.reg
在X86 32位架构下,ModRM字节中reg有3位,可以表示eax、ebx、ecx、edx、esi、edi、ebp、esp共8个寄存器。
但是,在X64架构下,有rax、rbx、rcx、rdx、rsi、rdi、rbp、rsp、r8、r9、r10、r11、r12、r13、r14、r15共16个寄存器,reg原有的3位不够用了,因此,需要与REX prefix中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 |
- 扩展ModRM.r/m
在X86 32位架构下,ModRM字节中r/m有3位,可以表示8种内存寻址方式。
但是,在X64架构下,由于寄存器数量翻倍,原来的3位不足以表示所有寄存器的寻址方式,因此,需要与REX prefix中B位合并扩展为4位Bbbb,并且与X86 32位架构下一样,再次与ModRM.mod组合,表示64种内存寻址方式,如下所示:
2.2 第二种,寄存器到寄存器的寻址(无内存操作数,mod=11)
比如:mov r9,r8
,机器码:4D 8B C8
ModRM.reg与ModRM.r/m的扩展与上文描述完全一致,不再赘述。
2.3 第三种,带SIB字节的内存寻址(mod!=11 & r/m=100)
比如:mov rax,[rbx+rcx*2]
,机器码:48 8B 04 4B
- 扩展ModRM.reg
ModRM.reg的扩展与上文描述完全一致,不再赘述。 - 扩展SIB.index
以下为32位时,SIB的寻址地址计算。
也是由于X64下,寄存器数量增加,index只有3位,只能表示8个寄存器。因此需要将REX.X与index合并为4位Xxxx,可以表示16个寄存器。Xxxx为X64架构下寄存器的编号。 - 扩展SIB.base
X64下,寄存器数量增加,base只有3位,只能表示8个寄存器。因此需要将REX.B与base合并为4位Bbbb,可以表示16个寄存器。Bbbb为X64架构下寄存器的编号。
2.4 第四种,寄存器操作数编码于操作码字节中
比如:mov rax,4
,机器码:48 C7 C0 04 00 00 00
需要将REX.B与reg合并为4位Bbbb,可以表示16个寄存器。Bbbb为X64架构下寄存器的编号。
3 RIP寻址
X64架构的寻址模式与X86类似,但不完全等同,X64架构提供了一种新的rip相对寻址模式(RIP-Relative),引用单常量地址的指令,被编码为基于rip的偏移量(offsets)。
例如:
mov rax, [addr] ;指令移动以addr+rip为起始地址的8字节内容到rax寄存器。
在64位模式下,RIP-Relative寻址,实现了一种新的寻址形式,rip相对(相对指令指针)寻址。有效的地址,是通过在下一条指令的64位RIP上增加位移来形成的。在IA-32体系结构和兼容模式下,相对于指令指针的寻址只适用于控制转移指令。
在64位模式下,使用ModR/M寻址的指令可以使用rip相对寻址。没有rip相对寻址,所有ModR/M指令模式都相对于零寻址内存。
RIP相对寻址允许特定的ModR/M模式使用带符号的32位位移来寻址相对于64位RIP的内存。这提供了与RIP的±2GB的偏移范围。rip相对寻址的ModR/M和SIB编码如下表所示。在当前的ModR/M和SIB编码中存在32位位移寻址的冗余形式。有一种ModR/M编码和几种SIB编码。rip相关寻址使用冗余形式进行编码。在64位模式下,ModR/M Disp32(32位位移)编码被重新定义为RIP+ Disp32,而不仅是位移。
rip相对寻址的ModR/M编码不依赖于使用前缀。具体来说,101B的r/m位域编码(用于选择rip相对寻址)不受REX前缀的影响。例如,选择“R13”(REX.B = 1, r/m = 101B)时,只要mod = 00B仍然会导致rip相对寻址。REX.B结合ModR/ m的4位r/m字段没有被完全解码。为了处理没有位移的R13,软件必须使用1字节的位移0对R13 + 0进行编码。
rip相对寻址由64位模式启用,而不是64位的address-size。使用address-size前缀不会禁用rip相对寻址。地址大小前缀的作用是截断并零扩展计算出的有效地址为32位。