3. Prefixes - Part I

To be, or not to be: that is the question.

-- William Shakespeare, "HAMLET".

Hello, Prefixes!

就像经典的“Hello World!”程序一样,让我们也从最简单的一个实例看起:

OpCode && mnemonic
OpCodemnemonic
40INC EAX
66 40INC AX

假设默认的操作数是32位,我们就可以得到上表的结果。(为什么默认是32位?看到后面就会明白的)

我们可以看到,40表示的是INC EAX66 40表示的是INC AX,两者的分别在于:前者的操作数是32位的(EAX),而后者是16位的(AX)。

从OpCode的角度来看,后者比前者多了一个66,就导致了不同的结果,唔……Intel x86规定:

66是一个Prefix,我们把Prefix翻译为前缀,所谓前缀,就是与code进行组合用以产生出某些变化形式的“东西”。唔……好拗口啊,真不好解释,请看晕了的朋友不要抛砖头,继续往下阅读吧。

认识 Prefixes

回忆一下第一章中介绍的OpCode的6个域:

  1. Prefixes
  2. code
  3. ModR/M
  4. SIB
  5. Displacement
  6. Immediate

记住:

Prefixes是所有的域中最容易理解的一个,请先明了它的一些特性:

Prefixes的几个特性

1. 它是唯一的一个可能出现在code之前的域。
2. 所有的Prefixes都只有1个字节。
3. 在一个OpCode中可能会有多个Prefixes

看看刚才提到过的prefix 66,这个prefix的意思是“切换默认的操作数的大小”。例如在有的系统中有2种默认的操作数大小:16位和32位。操作数有可能会被写成16位或者32位,唯一的区分方法是看它有没有prefix 66

唔……是不是讲得不够清楚呢?我们来看看:

OpCode && mnemonic
OpCodemnemonic
66 ADLODSW
ADLODSD

依然假设默认的操作数是32位的,有没有发现什么不寻常的地方?

LODSWLODSDcode域是一样的——都是AD!其实,LODSWLODSD这两个指令是同一个指令,只不过它们的操作数大小不一样——LODSW使用了2个字节(16位)的WORD作为操作数,而LODSD则使用了4个字节(32位)的DWORD作为操作数。

看到这里,读者应该能够明白了:prefix 66的作用是切换默认的操作数大小。请注意我们并没有说“指定”,而是“切换”!反映到这个例子中,就是“切换默认的32位操作数到16位”,而不是“指定操作数的大小为16位”。

这点非常重要!!!绝对不是在玩文字游戏!!

如果默认的操作数大小是WORD16位),那么切换后就是DWORD32位);反之,如果默认的操作数大小是DWORD32位),那么切换后就是WORD16位)。

切记!Prefixes 66就像一个触发器一样,起的作用就是进行切换。

让我们再来看一个特例:

B0 FF    MOV AL, 0FF
8A C1    MOV AL, CL

看清楚了吗?现在的操作数是ALCL,加上prefix 66后会如何?

66 B0 FF    MOV AL, 0FF
66 8A C1    MOV AL, CL

Faint!没有任何变化!

为什么呢?我们可以猜测一下:也许并不是所有情况下的操作数大小都可以随意改变的。假如这个改变是不允许的,那么它就会被忽略。

为了证实这个猜想,让我们来看看下一个更有趣的例子:

prefix F3(rep)的作用是让CPU对随后的指令循环执行ecx(cx)次,指令INC EAX的OpCode是40,好,如果我们想连续执行3次INC EAX的话,应该怎么样呢?

也许你会想当然地认为应该这样写:

xor eax, eax
mov ecx, 3
rep inc eax

实际上!并不是这样!这样的程序的运行结果是:

  1. 没有任何异常(exception)产生。
  2. 最后eax = 1,这意味着prefix F3并没有起作用——它被忽略了。

现在我们可以证实之前的想法:

如果Prefixes不能对随它之后的OpCode起作用,那么它就会被忽略。

再回忆一下之前提到的三个特性:

  1. Prefixes是唯一的一个可能出现在code之前的域。
  2. 所有的Prefixes都只有1个字节。
  3. 在一个OpCode中可能会有多个Prefixes

前面两点应该比较容易理解,让我们来看看第3点是什么意思。

如果想得到下面的指令:

REP LODSW

它的OpCode将会是:

66 F3 AD

解释如下:

66 AD:LODSW
F3: REP

都是前面讲过的内容,不难吧?只是组合起来使用罢了。

不过……细心的读者可能会问:为什么要把66放在第一位,把F3放在第二位呢?把它们的位置调换一下行不行?答案是:行!事实上它也可以写成:

F3 66 AD

效果是一样的!

Prefixes的特性

一个OpCode中可以有多个Prefixes。
如果有多个Prefixes,那么它们的顺序可以打乱,不会有任何问题。

最后,我们还可以得出一个推论:

由于每个Prefixes会多占用1个字节,所以也必定会导致处理器多使用一个指令周期进行解码——无论在时间还是空间上都会造成浪费。因此,我们应该权衡在哪些场合才使用Prefixes,如非必要,应该减少对它的使用。

Is it ALL?

Of course not! 由于章节的篇幅问题,Prefixes的进一步讲解会放在后面的章节中继续进行,我们会看到更多的有关Prefixes的信息。



(注:如果出现链接打不开的情况,请去掉IE浏览器的“工具->Internet选项->高级->总是以UTF-8发送URL”前面的勾。谢谢!)