第七部分 立即数

    立即数是Intel指令的中操作对象的一种(加上寄存器、内存共三种)。作为指令的一部分,立即数必须是直接可用的数字,这是立即数跟其他两种操作对象的主要区别(寄存器操作对象,CPU需要首先定位到寄存器,然后再读或写该寄存器中的对象。CPU指令的执行过程可粗分为:取指令,指令解码,取操作对象, ALU运算,存结果,取下一条指令……,立即数跟其他两种操作对象的区别体现在“取操作对象”这一步。立即数需要0个时钟周期,解码之后,立即数就可以使用了,立即立即,这就是立即的涵义所在;寄存器操作对象需要1个时钟周期;内存操作对象根据寻址方式的不同可能需要2-3或更多个时钟周期)。

    7.1 立即数的解析

    立即数的解析相对来很简单。立即数有一些很明显的特性:
    (1) 立即数只有大小的分别。一般情况,我们也把改变EIP的指令中的常数作为立即数来处理。比如jmp rel8/16 或者 call 16:32……,这里相对偏移地址的大小与EIP/IP大小不一致的时候,一般需要进行符号位扩展。(前面曾经提到过指令的操作数大小一般必须相同,这点有特殊情况存在的,操作数大小扩展指令的操作对象就不一样,例如movcx, movzx, cwd, cdq等等指令)。

    (2) 立即数只能做为源操作数而不能作为目的操作数。这点是很明确的,目的操作对象只能是寄存器或者内存。

    立即数的解析只需要知道需要解析的立即数的大小就可以了,是几个字节读几个字节就行了。这里需要注意的一个重要的问题就是立即数需不需要进行符号为扩展的问题,或者更简单点来说,就是立即数是signed类型的还是unsigned的类型的。这就需要对指令的另外一个特殊的位进行解析了,那就是s位。

    7.2 s位
    
    先来看看下面的一组指令:

    00401000      80C0 F8             add     al, 0F8
    00401003      81C0 78563412       add     eax, 12345678
    00401009      82C0 F8             add     al, -8    实际为: add al, F8
    0040100C      83C0 F8             add     eax, -8    实际为: add eax, FFFFFFF8

    看看80,81,82,83指令的定义:(括号中表示ModR/M中的Reg/Opcode辅助编码部分)
    80(0)  add  r/m8,    imm8
    81(0)  add  r/m16/32/64,  imm16/32
    82(0)  add  r/m8,    imm8
    83(0)  add  r/m16/32/64,  imm8
    
    16/32位的立即数不存在扩展之分,这里主要的区别在8位的立即数。很明显,82中的立即数是signed类型的,而80的是unsigned类型的,83实际为add eax, FFFFFFF8(因为指令的操作对象必须大小一致),需要把8位的立即数进行符号位扩展成32位。我们看看他们的编码区别:

    指令   编码       非立即数操作对象大小  立即数
    ----------------------------------------------
    80  1000 00[0]0  8位    8位(unsigned)   
    81  1000 00[0]1  16/32位    16/32位
    82  1000 00[1]0  8位    8位(signed)    
    83  1000 00[1]1  16/32位    8位(signed extern to 32)

    这里我们首先能很明显地看到w位的存在,w位决定了内存编码的操作对象的大小,这里就毋须多提了。我们可以看到,立即数需不需要进行符号位扩展,跟指令的第二位密切相关,上面已经用方括号标记出来了。当指令的第二位为1的时候,指令中的立即数需要进行符号位扩展(扩展到多少位,就看另外一个操作数的大小了,比如上面82,另外一个操作数就8位,立即数需要作0位扩展,不变,但是最高位就是符号位,此时的立即数变成了一个8位的符号数。83,另外一个操作数为32位,那么8位的立即数就需要符号扩展成32位,即FFFFFFF8),否则就不进行符号位扩展。这个位就是s位。

    这里我们看到,81和83实际上进行的是相同的操作,例如:

    0040100F      81C0 F8FFFFFF       add     eax, -8

    由于32位的无所谓扩展问题,83只是81的子集,然而83有着更短的指令编码。

    一般来说,当指令中存在立即数,而且立即数作为运算对象的时候,s位才有定义。

    比如:
    0040101E      CA 12FF             retf    0FF12
    CA  rentf  imm16
    s位 = 1但是并无扩展之说。

    7.3 立即数的解析代码

    立即数的解析相对来说很容易,这里需要知道待解析的立即数的大小,立即数的s位的值,和是否考虑s位进行符号位扩展。需要说明的是,立即数的解析部分只处理立即数,而相对偏移地址等,在指令的识别/解析过程中进行。

    相关代码下载:

    immediate.rar
    
    测试结果:

    

    od中检验一下:

    
   
    在做32位的运算时,FFFFFFF8 跟 -8是没有任何区别的,FFFFFFF8实际上是-8在计算机中的补码表示,这里如何翻译,就取决于需要何种表示了。