首先要清楚,odex只是对代码段(我将dex文件与elf文件类比,大家都将执行文件分成不同的段)作优化,而其它用于类反射信息的段都应用原来的dex,所以odex文件内部还包含了一个dex。
打开一个dex或一个odex文件,就是要将其中用于类反射的信息加载到虚拟机运行时中。对于打开一个odex文件,目的也是要将其中包含的dex部分的信息进行加载。
dalvik(libdvm.so)打开dex文件,实质就是要将dex里的反射信息加载到一个DexFile组织的结构体。这个结构有一个查找表,DexClassLookup。这个结构不直接被 libdvm.so 使用,而是由一个上层的DvmDex代替,为libdvm.so其它函数调用使用。但这个DvmDex不被JNI层,也就是libdvm.so最接近java的一层所直接使用,这里有一个DexOrJar结构体关联DvmDex,DexOrJar为最上层可寻找到的对象,并且插入到hash表中。
DexOrJar,JNI 层
DvmDex,libdvm 内部函数
DexFile,最后的执行委托处
对于dalvik.system.DexFile.openDexFile,它的native实现Dalvik_dalvik_system_DexFile_openDexFile的工作,就是调用libdvm内部相关的函数完成,从dex文件加载进DexFile,分配DvmDex关联DexFile,最后用一个DexOrJar关联DvmDex,并将DexOrJar放入hash表中。
我们c/c++则可以直接绕开java层或jni,直接去调用libdvm.so的内部函数去进行打开,以及打开后的使用。这些内部函数使用的是DvmDex。
在内部支持的打开函数里面,最上层的是dvmRawDexFileOpen,也就是Dalvik_dalvik_system_DexFile_openDexFile直接调用的内部函数。这个函数完成了比较多的事,如创建odex,优化dex进odex,然后才是实质上的打开。
实质上去打开dex的内部函数分别有dvmDexFileOpenFromFd以及dvmDexFileOpenPartial。dvmDexFileOpenFromFd的参数是odex的文件描述符,dvmDexFileOpenPartial的参数是odex或dex的映像。这两个函数都会返回DvmDex。并且最终使用dexFileParse实质地将dex的反射内容加载进DexFile。
dvmRawDexFileOpen,最上层的内部打开函数
dvmDexFileOpenPartial和dvmDexFileOpenFromFd,返回DvmDex,可用于其它内部函数调用
dexFileParse,最低端,也就是最实质的打开函数
使用dvmDefineClass就可是直接将类从Dex加载到ClassLoader,并不需要经过Java层或Jni来进行类的加载。
在libdvm.so只有少数几个函数是用extern "C"风格的链接符号导出的,dalvik却是c++项目,也就是说其它符号都使用了c++的风格,所以你并不能直接通过直观的函数名来进行函数符号的获取,而必须使用它对应的c++风格的真实链接符号。
下面是我将python移植后使用的pyjni.py,使用ctypes获取符号将名字做映射。这些符号可以通过objdump取得。
这样我就能在程序运行的同时手动去使用libdvm.so的内部函数,像命令一样使用,而不用每次都进行ndk编译。