标签:
环境:android 4.4.3下面主要拿c语言版本的dalvik解释器进行分析。源码的路径在dalvik/vm/mterp/out/InterpC-portable.cpp。
dalvik真正对指令解析是从dvmInterpretPortable(Thread* self)函数开始的。
下面对几个最重要的变量进行说明:
curMethod*:当前执行的方法。对应的结构体源码在dalvik/vm/oo/Object.h。
fp:这就是dalvik用来模拟寄存器的指针。
pc:pc就是用来取dalvik指令的。
inst:当前执行的指令。通过fetch(0)这个宏来获取的,注意它是2个字节大小。如果大于2个字节,就必须再次通过fetch(_offset)来取源操作数或者目的操作数,fetch(_offset)定义为:#define FETCH(_offset) (pc[(_offset)])。每一条指令执行完成之后会通过FINSH(_Offset)这个宏来调节pc,使其指向下一条指令的地址。
retval:与函数返回值有关。
methodClassDex:看赋值过程就能明白。从当前执行的方法找到当前方法所在的类,从所在的类找到它所属的dex。
DEFINE_GOTO_TABLE(handlerTable);这个宏是非常重要的。这个宏也定义在dalvik/libdex/DexOpcodes.h,
[ATTACH]99647[/ATTACH]
经过宏替换,相当于定义了一个数组名为handlerTable的数组,里面的每一个数据也是宏,宏的定义就在dalvik/vm/mterp/out/InterpC-portable.cpp文件中。
经过宏替换,相当于定义了一个数组名为handlerTable的数组,里面的每一个数据也是宏,宏的定义就在dalvik/vm/mterp/out/InterpC-portable.cpp文件中。
# define H(_op) &&op_##_op。##就是进行参数连接。最终经过宏展开后,该数组的第一个数据为:&&op_OP_NOP。其余的一样进行替换。为啥里面的数据都是void*类型,实际是因为&& 之后op_OP_NOP是标号。
下面额外说明一个关于标号的用法。因为dalvik解释器每一条指令都是goto 标号去执行的。
这个程序就会输出2222222222222 这一行。
接着对dvmInterpretPortable(Thread* self) 函数继续分析:
FINISH(0)就是第一条指令进行解析了。
ADJUST_PC(_offset)这个宏的作用实际上就是用来调节pc的位置,使pc每次指向我们要执行指令的位置。FETCH(0)前面已经说过,获得这条指令的前两个字节赋给inst。下面看goto *handlerTable[INST_INST(inst)]这句. INST_INST(inst)也是一个宏。宏定义:#define INST_INST(_inst) ((_inst) & 0xff) 可知,这是用来取操作码的。而操作码的数值实际上跟前面说的handlerTable索引是一一对应的。假设此时操作码值为0,根据前面提到的标号用法,此时goto *handlerTable[INST_INST(inst)]就是goto op_OP_NOP
接着FINISH(0)往下看,下面就是每一条指令如何执行的宏。比如第一条指令为
HANDLE_OPCODE(OP_NOP) 同样也是一个宏。宏定义为:#define HANDLE_OPCODE(_op) op_##_op:进行替换就成了op_OP_NOP:。这就跟前面的goto连上了。当然NOP是什么都不做,直接FINISH(1)调节pc的位置获取下一条指令了。
每一条指令执行完成之后都会调用FINISH这个宏。通过前面执行完的指令大小,设置对应的FINISH宏参数,调节pc位置。
这个就是一个最简单的指令分析了,可能还有一些细节性的东西没有说清楚,还请见谅。对于其它复杂的指令,大家也可以对着分析,就会发现更细致的东西,比如fp指针是如何模拟寄存器的等等。
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/sherry_dl/article/details/48001777