5. ModRM - Part I |
转自老罗
道,可道,非常道。
-- 老子,《道德经》
基本概念
让我们从最经常用到的域开始学起——ModR/M。
在开始之前,先来讲一些最基础的概念,扫扫盲。
一个字节如果被转换成二进制,则是由8位(bit)来表示(不足8位的话则高位用0来补足),例如:
16进制 2进制
B7 1011 0111
3A 0011 1010
示例中的B7的二进制是1011 0111,这是典型的4:4表示格式——1011表示的是B,0111表示的是7,这样,1011 0111表示的就是B7了。
很容易理解吧?呵呵,那么我们可不可以用另外一种方式来表示一个字节呢?
答案是肯定的:
16进制 2进制的4:4格式 2进制的2:3:3格式
B7 1011 0111 10 110 111
3A 0011 1010 00 111 010
请看,我们在这里引进了一种新的表示格式:2:3:3
它的特点是把一个字节的8位二进制分成3个部分:最高的2位表示的是一个东西,接下来的3位表示的是另外一个东西,以及最后的3位表示的是另另外一个东西。
好了,明白了这一点后,我们来开始吧!
首先回忆一下OpCode的组成格式:
- Prefixes
- code
- ModR/M
- SIB
- Displacement
- Immediate
请注意第三项:ModR/M,它占一个字节,其格式为:
7 6 5 3 2 0
Mod Reg/Opcode R/M
可见,ModR/M是由Mod、Reg/Opcode和R/M三个部分组成的。每个部分所占的bit大小为:
Mod: 占最高位的6~7共2个bit
Reg/Opcode: 占中间位的3~5共3个bit
R/M: 占最低位的0~2共3个bit
呵呵,正好是2:3:3的格式!
ModR/M的具体描述如下面两个表,第一个是16位的,第二个是32位的:
(截图自《IA-32 Intel Architecture Software Developer's Manual Volume 2: Instruction Set Reference》,页码2-5,2-6,希望Intel不要因为版权问题找我的麻烦)
- 16位 -
- 32位 -
哇,这两个图好复杂呀!你是不是已经有了这个感慨了呢?
呵呵不要紧,让我们来举个例子看看ModR/M到底是怎么来看的:
mov edi, ecx 8B F9
sub edi, ecx 2B F9
注意这两个OpCode的第二个字节——都是F9,再来看看OpCode的格式:
Prefixes Code ModR/M SIB Displacement Immediate
我们在前面说过,在OpCode的格式中,只有Code是必须有的,别的都是可选的。所以,在8B F9和2B F9这两组OpCode中,8B和2B就是Code,(这里没有Prefixes,因为Prefixes只有在前面的章节中所介绍过的那几个:66、67、F2、F3、2E、36、3E、26、64、65、F0)。
紧接着在Code后面的就是ModR/M了,所以在这两组OpCode中的F9就是ModR/M。(在这里也没有SIB、Displacement和Immediate)
F9的4:4格式的二进制是1111 1001,我们把它分解成2:3:3的二进制看看:
16进制 2进制的2:3:3格式
F9 11 111 001
也就是:
Mod: 11
Reg/Opcode: 111
R/M: 001
晕头转向了?呵呵,让我们来对其分而治之吧!
假设是在32位模式下。从上面的第二个图中可以看到,Mod总共分为00、01、10、11四种情况,每种情况又分别有8种情况。现在Mod是11,所以我们应该看Mod为11的那一栏。
OK,现在来讨论第二个:Reg/Opcode
Reg/Opcode中间的那个“/”表示“或”,意思就是,这个地方可以表示为Reg或者Opcode——至于到底什么时候表示Reg,什么时候表示Opcode,这就要由Code来决定了。目前我们不必去深究它,后面会讲明白的,我们只要知道,如果它是表示Opcode,则这个指令必定是2个字节的。
Reg由3个bit的二进制组成,因此,它可以表示:
2 ^ 3 = 8
一共8种可能的值。我们知道,常用的通用寄存器恰好也有8个,因此,根据组合数学的常识,可以得到:
REG && Register | |||||||||||||||||
REG | Register | ||||||||||||||||
000 | EAX | ||||||||||||||||
001 | ECX | ||||||||||||||||
010 | EDX | ||||||||||||||||
011 | EBX | ||||||||||||||||
100 | ESP | ||||||||||||||||
101 | EBP | ||||||||||||||||
110 | ESI | ||||||||||||||||
111 | EDI |
这是在32位的模式下得到的。
在16位的模式下,Reg则是表示另外一种“局部”的格式,它的低4位表示寄存器的低地址,高4位表示寄存器的高地址,如下表:
REG && Register | |||||||||||||||||
REG | Register | ||||||||||||||||
000 | AL | ||||||||||||||||
001 | CL | ||||||||||||||||
010 | DL | ||||||||||||||||
011 | BL | ||||||||||||||||
100 | AH | ||||||||||||||||
101 | CH | ||||||||||||||||
110 | DH | ||||||||||||||||
111 | BH |
好了,把目光返回到上面的32位ModR/M图,看看最上面,在r32(/r)那一栏中,REG=111表示的就是寄存器EDI
到目前为止,最后剩下还没讨论的就是R/M。这一栏要与Mod结合起来。我们来看Mod为11的那一栏——R/M为001对应的寄存器是ECX
好了!大功告成!整理如下:
Mod: 11 表示应该查看Mod为11的那一栏
Reg/Opcode: 111 表示的是寄存器EDI
R/M: 001 表示的是ECX
因此,通过OpCode:
8B F9
2B F9
不难得到:
mov edi, ecx 8B F9
sub edi, ecx 2B F9
(注:8B是助记符“MOV”的Code,2B是助记符“SUB”的Code)
罗聪 www.LuoCong.com |
(注:如果出现链接打不开的情况,请去掉IE浏览器的“工具->Internet选项->高级->总是以UTF-8发送URL”前面的勾。谢谢!) |