《X86编码-指令格式与解析二》

4. ModRM 寻址模式

​ Opcode 对指令提供操作码,ModRM 最主要作用是对指令的 operands 提供寻址,另一个作用是对 Opcode 进行补充,而 SIB 则是对 ModRM 进行补充寻址。

有两种情况下是无需用 ModRM 提供寻址的:

(1)一部分操作数是寄存器的,它直接嵌入 Opcode 中。

(2)一部分操作数是立即数的,它直接嵌入指令编码中。

image-20210309221451886

以上图为例:

(1)C7 这个 Opcode 是 Group 属性,需要 ModRM.reg 来配合定位。

(2)ModRM.mod = 10 以提供 [SIB + disp32] 的寻址

(3)ModRM.reg = 000 表明 Opcode 是个 mov Ev, Iz

(4)SIB 由 ModRM.r/m = 100 来引导出

(5)REX.W = 1 使用 64 位的 operand

1.1、 ModRM.mod 寻址模式。

2 位组成 4 种寻址模式,总的来说,只有两种寻址模式,就是:内存寻址模式和寄存器寻址模式。 如下表所示:

ModRM.mod 寻址模式 描述
00 [base] 提供 [base] 形式的 memory 寻址
01 [base + disp8] 提供 [base + disp8] 形式的 memory 寻址
10 [base + disp32] 提供 [base + disp32] 形式的 memory 寻址
11 register 提供 register 寻址。

1.2、 ModRM.reg 寻址 register

3 位组成 8 个寄存器 ID 值,从 000 ~ 111,对应于 RAX、RCX、RDX、RBX、RSP、RBP、RSI 以及 RDI。

这个 ID 值可以被 REX.R 扩展为 4 位: 0000 ~ 1111

1.3、ModRM.r/m 寻址 register 或 memory

r/m 意即:registers/memory,提供对 register 或 memory 的寻址,它与 ModRM.mod 是密切相关的。如下表:

ModRM.mod ModRM.r/m ModRM.r/m 寻址  
REX.B = 0 REX.B = 1    
00 000 [rax] [r8]
001 [rcx] [r9]  
010 [rdx] [r10]  
011 [rbx] [r11]  
100 [SIB] [SIB]  
101 [disp32] 或 [rip + disp32] [disp32] 或 [rip + disp32]  
110 [rsi] [r14]  
111 [rdi] [r15]  
01 000 [rax + disp8] [r8 + disp8]
001 [rcx + disp8] [r9 + disp8]  
010 [rdx + disp8] [r10 + disp8]  
011 [rbx + disp8] [r11 + disp8]  
100 [SIB + disp8] [SIB + disp8]  
101 [rbp + disp8] [r13 + disp8]  
110 [rsi + disp8] [r14 + disp8]  
111 [rdi + disp8] [r15 + disp8]  
10 000 [rax + disp32] [r8 + disp32]
001 [rcx + disp32] [r9 + disp32]  
010 [rdx + disp32] [r10 + disp32]  
011 [rbx + disp32] [r11 + disp32]  
100 [SIB + disp32] [SIB + disp32]  
101 [rbp + disp32] [r13 + disp32]  
110 [rsi + disp32] [r14 + disp32]  
111 [rdi + disp32] [r15 + disp32]  
11 000 rax r8
001 rcx r9  
010 rdx r10  
011 rbx r11  
100 rsp r12  
101 rbp r13  
110 rsi r14  
111 rdi r15  

总结一下就是:

  1. ModRM.r/m = 0100 或 1100 时,它寻址的是 SIB 字节,引导出 SIB 字节,交由 SIB 进行补充寻址。

  2. ModRM.mod = 00 & ModRM.r/m = 0101 或 1101 时,在 32 位下它是 32 位的 displacment 值,在 64 位下它是 [rip + disp32] 寻址。
  3. REX.B 可以扩展 ModRM.r/m 为 4 位: 0000 ~ 1111

4.1 Opcode和ModRM 结合确定一条指令

4.1.1 一个操作数的 Opcode 定位

​ 操作数要么就是 registers,要么就是 memory,要么就是 Immediate 值。如果指令只有一个操作数。

(1)operand 是 register 的情况下

   在只有 1 个寄存器操作数的情况下,非 Group 属性的 Opcode,则 ModRM 无用武之处。所以,在这种情冲下,寄存器操作数绝大部分是嵌在 Opcode 里面,它由 Opcode 的寄存器域指出。如常见的 inc ecx、dec ecx、push eax 等。除了上述嵌入 Opcode 情况外,如果 ModRM 用于寻址 1 个操作数,这条指令的 Opcode 必定是:Group 属性。 (2)如它是 Immediate 的话,它绝对是无 ModRM。直接将 Immediate 值嵌入指令编码里

(3)如它是 memory 的话,它绝对是 Group 属性,需要 ModRM.reg 来配合定位。那为什么不能是直接 offset 呢,直接 offset 寻址留给最常用的,最有用的 Opcode,以免 Opcode 占位,浪费资源。

4.1.2 两个操作数的 Opcode 定位**

   两个操作数大部分都需 ModRM 配合定位寻址。ModRM 提供的 2 个操作数寻址大有用武之地。

​ (1)2 个操作数中,其中 1 个是寄存器,另1个不是立即数的这种情况最直接简单,由 ModRM 的 reg 及 r/m 提供寻址。 若其中 1 个是 GPRs 另 1 个是 immediate 或 displacement 的情形下更简单,GPRs 则直接由 Opcode 提供寻址。 ​ (2)2 个操作数中,没有寄存器的情形下,也就是要么是 memory,要么是 Immediate,它必然是个 Group 属性,reg 域提供 Opcode 的定位,r/m 提供内存寻址。Immediate 直接嵌入指令编码中。

4.1.2 三个操作数的 Opcode 定位**

   三个操作数中有一个必定是 Immediate,在 AMD 的 SSE5 指令集推出之前,x86 平台是无法提供第 3 个非 Immediate 操作数的定位。 直至 AMD 的 SSE5 通过增加另一个描述操作数的字节来寻址第 3 个操作数。

5. SIB 补充寻址

SIB 是对 ModRM 寻址的一个补充:

  1. ModRM 提供的是 registers 寻址、[register] 寻址(寄存器间接寻址)以及 [register + displacement](寄存器基址寻址)。

  2. SIB 提供的是 [base + index * scale] 这种形式的寻址。即:基址 + 变址

同样,SIB 是可选的,前面已经介绍:SIB 字节由 ModRM.r/m = 100 引导出来,指令中命名用了 [base + index] 这种地址形式时,必须使用 SIB 进行编码。

回到前面举的这个例子:

image-20210312193316748

(1)C7 这个 Opcode 是 Group 属性,需要 ModRM.reg 来配合定位。

(2)ModRM.mod = 10 以提供 [SIB + disp32] 的寻址

(3)ModRM.reg = 000 表明 Opcode 是个 mov Ev, Iz

(4)SIB 由 ModRM.r/m = 100 来引导出

(5)REX.W = 1 使用 64 位的 operand

5.1 SIB 的结构

表1: SIB 结构表

描述
SIB.scale [7:6] 提供 scale: 00 = 1, 01 = 2, 10 = 4, 11 = 8
SIB.index [5:3] 提供 index 寄存器
SIB.base [2:0] 提供 base 寄存器

上表所示 SIB 字节的组成部分为:scale-index-base 三个部分,按 2-3-3 的比例组成,在这整篇文档中的写法是:SIB.scale、SIB.index 以及 SIB.base

  1. SIB.scale 提供 index 寄存器乘数因子 scale
  2. SIB.index 提供 index 寄存器寻址。
  3. SIB.base 提供 base 寄存器寻址。

1.1、 用 SIB.index 寻址 index 寄存器

SIB.index 用来寻址 index 寄存器,可以使用 REX.X 进行扩展为 4 位编码

表2:SIB.index 寻址表

SIB.scale SIB.index 寻址模式  
REX.X = 0 REX.X = 1    
00 000 [rax + base] [r8 + base]
001 [rcx + base] [r9 + base]  
010 [rdx + base] [r10 + base]  
011 [rbx + base] [r11 + base]  
100 [base] [r12 + base]  
101 [rbp + base] [r13 + base]  
110 [rsi + base] [r14 + base]  
111 [rdi + base] [r15 + base]  
01 000 [rax * 2 + base] [r8 * 2 + base]
001 [rcx * 2 + base] [r9 * 2 + base]  
010 [rdx * 2 + base] [r10 * 2 + base]  
011 [rbx * 2 + base] [r11 * 2 + base]  
100 [base] [r12 * 2 + base]  
101 [rbp * 2 + base] [r13 * 2 + base]  
110 [rsi * 2 + base] [r14 * 2 + base]  
111 [rdi * 2 + base] [r15 * 2 + base]  
10 000 [rax * 4 + base] [r8 * 4 + base]
001 [rcx * 4 + base] [r9 * 4 + base]  
010 [rdx * 4 + base] [r10 * 4 + base]  
011 [rbx * 4 + base] [r11 * 4 + base]  
100 [base] [r12 * 4 + base]  
101 [rbp * 4 + base] [r13 * 4 + base]  
110 [rsi * 4 + base] [r14 * 4 + base]  
111 [rdi * 4 + base] [r15 * 4 + base]  
11 000 [rax * 8 + base] [r8 * 8 + base]
001 [rcx * 8 + base] [r9 * 8 + base]  
010 [rdx * 8 + base] [r10 * 8 + base]  
011 [rbx * 8 + base] [r11 * 8 + base]  
100 [base] [r12 * 8 + base]  
101 [rbp * 8 + base] [r13 * 8 + base]  
110 [rsi * 8 + base] [r14 * 8 + base]  
111 [rdi * 8 + base] [r15 * 8 + base]  

5.1 SIB.base 寻址 base 寄存器

SIB.base 用来寻址 base 寄存器,可以用 REX.B 扩展为 4 编码

表3: SIB.base 寻址表

REX.B ModRM.mod SIB.base              
000 001 010 011 100 101 110 111    
0 00 rax rcx rdx rbx rsp disp32 rsi rdi
01 rbp+disp8                
10 rbp+disp32                
1 00 r8 r9 r10 r11 r12 disp32 r14 r15
01 r13+disp8                
10 r13+disp32                

这个表与上面的表1 SIB.index 表是紧密相关的,而且还受到 ModRM.mod 的影响

SIB 的使用须由 ModRM 引导出:[SIB]、[SIB + disp8] 以及 [SIB + disp32] 形式,

分别由 ModRM = 00-XXX-100、ModRM = 01-XXX-100 以及 ModRM = 10-XXX-100 引出。

Reference

【1】https://nju-projectn.github.io/i386-manual/appa.htm

【2】https://nju-projectn.github.io/ics-pa-gitbook/ics2020/i386-intro.html

【3】https://blog.csdn.net/xfcyhuang/article/details/6230542