7. displacement 值
displacement 是 ModRM 地址寻址里的一部分,displacement 为 base 或 index 提供一个 offset 值。
因此:在指令的编码中必须要提供 ModRM 字节,需要的时候还要提供 SIB 字节。
例如指令:mov edx, dword ptr [0x08040400]
它的指令编码是:8b 15 00 04 04 08
这条指令的 displacement 虽然是嵌在 encode 中,但它不属于 immediate 的概念,它是需要通过 ModRM.r/m 的寻址。
指令:mov eax, dword ptr [0x08040400]
它的指令编码可以译为:a1 00 04 04 08 (当绝对地址与 eax/rax 寄存器之间的寻址,它可以使用这种编码方式)
在这种情况下,它不需要 ModRM 字节进行寻址,这个地址值是 immediate 概念。
当然,这条指令也可以译为:8b 05 00 04 04 08 (通过 ModRM 进行寻址),此时它就是 displacement 部分。
由 ModRM.mod 来控制 displacement:
(1)提供绝对地址方式
ModRM |
SIB |
寻址 |
说明 |
|||||
mod |
reg |
r/m |
scale |
index |
base |
|||
1 |
00 |
XXX |
101 |
--- |
ModRM = [disp32] |
非 64 位模式下,只需 ModRM 字节寻址 |
||
2 |
00 |
XXX |
100 |
XX |
100 |
101 |
ModRM + SIB = [disp32] |
ModRM 字节配合 SIB 字节寻址 |
上表是提供绝对地址寻址的两种方式。表格中的 XX/XXX 表示可以为任意值。
注意方式 1 中,提供的 [disp32] 是在 非 64 位模式下。
(2)为 base 提供一个 offset 值
ModRM |
SIB |
寻址 |
说明 |
|||||
mod |
reg |
r/m |
scale |
index |
base |
|||
1 |
01 |
XXX |
≠100 |
---
|
ModRM = [base + disp8] |
给 base 提供一个 8 位的 displacement |
||
2 |
10 |
XXX |
≠100 |
--- |
ModRM = [base + disp32] |
给 base 提供一个 32 位的 displacement |
||
3 |
01 |
XXX |
100 |
XX |
100 |
101 |
ModRM + SIB = [base + disp8] |
ModRM 字节配合 SIB 字节寻址 |
4 |
10 |
XXX |
100 |
XX |
100 |
101 |
ModRM + SIB = [base + disp32] |
ModRM 字节配合 SIB 字节寻址 |
可以提供 8 位和 32 位的 displacement 值。
表格中的 XX/XXX 表示该项为任意值,8 位和 32 位的 displacemnet 各有 2 种编码方式。
(3)为 index 提供一个 offset 值
ModRM |
SIB |
寻址 |
说明 |
|||||
mod |
reg |
r/m |
scale |
index |
base |
|||
1
|
00 |
XXX |
100 |
XX |
≠100 |
101 |
ModRM + SIB = [index * scale + disp32] |
ModRM 字节配合 SIB 字节寻址 |
ModRM 配合 SIB 寻址,这里 SIB.index ≠100 时,提供任意的 index 寄存器(除了 rsp 寄存器),可以为 index 提供一个 32 位的 displacement 值。
仅在这一种情况下才允许 index + base 寻址。
看下面的指令:
mov rax, qword ptr [rcx * 8 + 0x0c] |
它的 encode 是: 48 8b 04 cd 0c 00 00 00
它使用的 [base + index * 8] 寻址模式,在 SIB.base = 101(即:base = rbp) 的情况下,base 此时为 disp32
上面的有几种寻址方式,是依据 x32/x64 的两大原则,详见:ModRM 与 SIB 设计上的 2 个原则
仅在 64 位模式下,可以为 rip 提供一个 displacement 值
ModRM |
SIB |
寻址 |
说明 |
|||||
mod |
reg |
r/m |
scale |
index |
base |
|||
1 |
00 |
XXX |
101 |
---
|
ModRM = [rip + disp32] |
仅在 64 位模式下,可以提供 [rip + disp32] |
当在 64 位模式下,ModRM = 00-XXX-101 是 [rip + disp32] 寻址,这是 x64 体系中新增的一个寻址模式。
这种寻址模式很容易就得出 rip 值,如下指令:
lea rax, [rip] |
编译器会译出:8d 05 00 00 00 00 -----> lea rax, [rip + 0x00000000]
大部分情况下,displacement 是 signed(符号数),在绝对地址情况下,它是 unsigned(无符号数)
(1)displacement 是 signed(符号数)
无论是为 base 还 index 提供一个 offset 值,此时 displacement 都是 singed(符号数),那么将会按 singed 的计算方式,计算结果。
在这种情况下,当 displacement 是 disp8 时,它会被符号扩展到 address size(取决于 effective address size)
关于 effective address size 的详见:default(缺省)与 effective(有效)
(2) displacement 是 unsigned(无符号数)
当 displacement 是绝对地址是,它是 unsigned(无符号数)。
例如: mov edx, dword ptr [0x08040400] 此时 displacement 是一个 unsigned(无符号数)
在 16 位下 displacement 分为 8 位和 16 位,在 32 位下分为 8 位和 32 位,而在 64 位下同样只有 8 位和 32 位。
4.1、 displacement 的 signed-extended(符号扩展)
虽然有 8 位的 displacement,实际上,都会被 processor 进行 signed-extended 符号扩展到 address size
当 displacement size 是小于 address size 的,这个 displacement 就会被 singed-extended 到 address size
这个 address size 是指令当前的 address size,它取决于 effective address size。
★ 没有经过 address size override 的,address size 就是它的 default address size
★ 经过 address size override 后,address size 就等于 override 的 address size
关于 effective address size 的详见:default(缺省)与 effective(有效)
4.2、 x64 体系的 64 位下的 displacement
64 位的 default address size 是 64 位,在没有 address size override 的,无论 disp8 还是 disp32 都会被扩展到 64 位地址上。
这里就产生了一些疑问,在 x86/x64 的指令格式中,displacement 是没有 64 位的。
★ 到度有没有 64 位的 displacement 呢?
★ 没有的话,怎么使用 64 位绝对地址呢?
★ 有的话,什么情况下才会出现 64 位的 displacement 呢?
在 x64 体系的的编码中,确实没有 64 位 displacement 这回事,但是并不表示 64 位下不能使用 64 位的绝对地址。 这个 64 位的绝对地址并不是 displacement 的意思。
例子: mov rax, qword ptr [0xffff800008040400]
这条指令是正确的,而且它使用了 64 位的绝对地址。
它的 encode 是:48 a1 00 04 04 08 00 08 ff ff
这条指令的 Opcode 是 a1,指令形式为:MOV rAX, Ov
second operand 是 O 表示它将是一个直接的地址形式,这个 operand 是允许使用 64 位的绝对地址,而不需要 ModRM 寻址。
在这里地址值 [0xffff800008040400] 它是以 immedeate 值形式嵌在 encode 中,它属于 immediate 部分,而非 displacement 值。
同样,指令:mov qword ptr [0xffff800008040400], rax 也是正确的
它的 encode 是:48 a3 00 04 04 08 00 08 ff ff
另一个例子是:mov rcx, qword ptr [0xffff800008040400]
这条指令是错误的,它不可能产生 encode,opcode a1 是仅仅对 rax 寄存器可用,因此:只有 rax 寄存器才能使用 64 位的绝对地址值。
4.3、 Opcode a1 和 a3 可以使用 64 位绝对地址的本质
这两个 Opcode 的 operand 嵌入 encode 中,它不依赖于 ModRM 寻址。 在这种情况下,可以设计使用 64 位的地址值(immediate)。
因此:
设计使用 64 位地址的前提条件必须是: operand 不依赖于 ModRM 寻址,换句说说:operand 是嵌在 Opcode 里的。
4.4、 不能使用 64 位 displacement 的本质
我们来看一看,是什么原因限制了 64 位 displacement 的使用。
例子: mov qword ptr [rax + rcx * 8 + 0x11223344], 0x12345678
这个例子是前面多次提到过,它的 encode 是:48 c7 84 c8 44 33 22 11 78 56 34 12 (共 12 个 bytes)
可以看出,限制 64 位 displacement 使用的原因就是:指令长度的限制
由于 x86/x64 的指令长度限制 15 bytes, 如果上面这条指令可以使用 64 位 displacement 的话,指令长度超过了 15 bytes。
因此:
使用 64 位 displacement 第 2 个前提条件是:指令长度不能超过 15 bytes
4.5、 operand 类型为 O (直接地址值,不依赖于 ModRM 寻址)的最终设计前提就是前面所说的两个条件:
★ operand 不依赖于 ModRM 寻址,换句说说:operand 是嵌在 Opcode 里的。
★ 指令长度不能超过 15 bytes
那么:能不能为 x64 体系扩展指令格式为更长的格式呢?
显然,x64 体系设计者们没有采取这种方式,出于兼容的大提前下,是不可能有这种考虑的。
mik(deng zhi)写于 2008-11-26 15:40