5. ModRM 寻址模式
Opcode 对指令提供操作码,ModRM 最主要作用是对指令的 operands 提供寻址,另一个作用是对 Opcode 进行补充,而 SIB 则是对 ModRM 进行补充寻址 |
回到前面举的这个例子:
REX prefix |
Opcode |
ModRM |
SIB |
displacement |
immediate |
||||||||
4 |
W |
R |
X |
B |
mod |
reg |
r/m |
scale |
index |
base |
|||
0100 |
1 |
0 |
0 |
0 |
C7 |
10 |
000 |
100 |
11 |
001 |
000 |
44 33 22 11 |
78 56 34 12 |
(1)C7 这个 Opcode 是 Group 属性,需要 ModRM.reg 来配合定位。
(2)ModRM.mod = 10 结合 ModRM.r/m = 100 提供 [SIB + disp32] 的寻址
(3)ModRM.reg = 000 表明 Opcode 是个 mov Ev, Iz
(4)SIB 由 ModRM.r/m = 100 来引导出
(5)REX.W = 1 使用 64 位的 operand
1、ModRM 的结构
-- |
位 |
描述 |
ModRM.mod |
[7:6] |
提供寻址模式: 11 = register 00, 01, 10 = memory |
ModRM.reg |
[5:3] |
提供 register 或者对 Opcode 进行补充 |
ModRM.r/m |
[2:0] |
提供 register 或者 memory 依赖于 ModRM.mod |
上表所示 ModRM 字节的组成部分为:mod-reg-r/m 三个部分,按 2-3-3 的比例组成,在这整篇文档中的写法是:ModRM.mod、ModRM.reg 以及 ModRM.r/m。
★ ModRM.mod 提供寻址的模式,这个模式以 displacement 值作区别的。当 ModRM.mod = 11 时,它提供 register 寻址。
★ ModRM.reg 提供寄存器寻址,reg 表示寄存器 ID 值,或者对 Group 属性的 Opcode 进行补充。
★ ModRM.r/m 提供 register 寻址或 memory 寻址,取决于 ModRM.mod 值:ModRM.mod = 11 时,ModRM.r/m 提供寄存器的 ID 值。
ModRM.mod |
寻址模式 |
描述 |
00 |
[base] |
提供 [base] 形式的 memory 寻址 |
01 |
[base + disp8] |
提供 [base + disp8] 形式的 memory 寻址 |
10 |
[base + disp32] |
提供 [base + disp32] 形式的 memory 寻址 |
11 |
register |
提供 register 寻址。 |
这个 ID 值可以被 REX.R 扩展为 4 位: 0000 ~ 1111(对应于 RAX 到 R15 寄存器)。
详见:寄存器编码表
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 |
上表所示:
★ ModRM.r/m = 0100 或 1100 时(扩展后的值),它寻址的是 SIB 字节,引导出 SIB 字节,交由 SIB 进行补充寻址。
★ ModRM.mod = 00 并且 ModRM.r/m = 0101 或 1101 时,在 32 位寻址模式下它是 32 位的 displacment 值,在 64 位寻址模式下它是 [rip + disp32] 寻址。
★ REX.B 可以扩展 ModRM.r/m 为 4 位: 0000 ~ 1111
按照 ModRM 编码规则,当 ModRM.r/m = 100,ModRM.r/m 应该是对应于 [rsp] 寻址。
事实上并非如此,ModRM.r/m 对应的是 SIB 寻址,由此而引导出 SIB 字节。指令系统设计者选择在 [rsp] 位置上用 SIB 进行替代。
这是因为:
★ 必须要有编码对应于 SIB 字节。
★ 用 SIB 代替 [rsp] 或许因为 [rsp] 相比其它的寻址并不那么常用。
2.1、那么怎样实现 [rsp] 寻址的编码?
既然在 ModRM 上无法满足 [rsp] 的寻址,那么只好在 SIB 里实现 [rsp] 的寻址。
在 SIB 里实现 [rsp] 寻址,是根据一个重要的原则: rsp 寄存器不能做为 index 寄存器,只能做 base 寄存器。
具体 SIB 的实现细节,详见:[rsp] 寻址的实现
同样情况下,按照 ModRM 编码规则,ModRM.r/m = 101 时,应该对应于 [rbp] 寻址。
事实上并非如此:当 ModRM.mod = 00 时,ModRM.r/m = 101 它对应的是 [disp32] 的寻址,在 64 位下,它是 [rip + disp32]。
指令系统的设计者们在 ModRM.r/m = 101 这个地方又进行了变通。
3.1、 那么怎样实现 [rbp] 寻址的编码呢?
在 [rbp] 寻址的实现上,又遵循另一个重要原则:rbp 作为 base 寄存器时,它必须以 [base + disp] 这种形式存在。
这个原则是建立在:rbp 意为 stack base pointer(基址指针),从语义上讲 base pointer 应该要加一个偏移量(displacement)
因此:[rbp] 这种形式在 encode 上是不存在的。
但是在 assembly(汇编语句)层面上是正确的,即:mov qword ptr [rbp], rax 是正确的语句。
具体 [rbp] 的实现,详见:[rbp] 寻址的实现
16 位寻址与 32 位寻址有较大的差异,16 位下是不支持 [base + index * scale + disp] 这种寻址模式的,这样在编码序列里就无需提供 SIB 字节了。
即:在 16 位下是无 SIB 字节的。
注:
16 位的 ModRM 寻址支持的是基址和变址寄存器间接寻址和 基址+变址寻址模式。基址寄存器 2 个:就是 bx 和 bp。变址寄存器也是 2 个:就是 si 和 di。
基于上述设计,基址+变址寻址的组合只有 4 个,也就是:[bx+si]、[bx+di]、[bp+si] 以及 [bp+di]。
ModRM.mod |
ModRM.r/m |
ModRM.r/m 寻址 |
00 |
000 |
[bx+si] |
001 |
[bx+di] |
|
010 |
[bp+si] |
|
011 |
[bp+di] |
|
100 |
[si] |
|
101 |
[di] |
|
110 |
[disp16] |
|
111 |
[bx] |
|
01 |
000 |
[bx+si+disp8] |
001 |
[bx+di+disp8] |
|
010 |
[bp+si+disp8] |
|
011 |
[bp+di+disp8] |
|
100 |
[si+disp8] |
|
101 |
[di+disp8] |
|
110 |
[bp+disp8] |
|
111 |
[bx+disp8] |
|
10 |
000 |
[bx+si+disp16] |
001 |
[bx+di+disp16] |
|
010 |
[bp+si+disp16] |
|
011 |
[bp+di+disp16] |
|
100 |
[si+disp16] |
|
101 |
[di+disp16] |
|
110 |
[bp+disp16] |
|
111 |
[bx+disp16] |
|
11 |
000 |
ax |
001 |
cx |
|
010 |
dx |
|
011 |
bx |
|
100 |
sp |
|
101 |
bp |
|
110 |
si |
|
111 |
di |
寄存器的 ID 取值为:0000 ~ 1111。由 REX.R 以及 REX.B 位进行扩展访问。
6、结合 Opcode 来看寻址模式及 Opcode 的定位
Opcode 定义指令的执行码,用于执行什么操作,对于操作数寻址上,Opcode 结合 ModRM 来定义操作数,这是一个经过反复琢磨推敲的过程,而最终又影响到 Opcode 的定位。
6.1、 一个操作数的 Opcode 定位
操作数要么就是 registers,要么就是 memory,要么就是 Immediate 值。如果指令只有一个操作数。
(1)operand 是 register 的情况下
除了上述嵌入 Opcode 情况外,如果 ModRM 用于寻址 1 个操作数,这条指令的 Opcode 必定是:Group 属性 |
前面已提过: Opcode 的分组是按 Operands 属性进行的。即:这一组 Opcode 拥有相同类型的 Operands。 这个类型的 Operands 主要是单个内存操作数或单个寄存器操作数而非嵌入 Opcode 中。 |
mik 写于 2008-11-26 15:40