虚拟机对于 Dex 文件的解析


3012162-f36fc362534bae04.png
数据结构

以下基于 Android_4.0.4源码进行分析。

先丢一张虚拟机用到的数据结构。我们的起点在
native private static int openDexFile(String sourceName, String outputName, int flags) throws IOException @ DexFile.java

//@dalvik/vm/native/dalvik_system_DexFile.cpp
static void Dalvik_dalvik_system_DexFile_openFile(const u4* args, JValue* pResult){
  RawDexFile* pJarFile;
  ...
  dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false);
  ...
}
//@dalvik/vm/RawDexFile.cpp
int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
  RawDexFile** ppRawDexFile, bool isBootstrap){
  DvmDex* pDvmDex = NULL;
  ...
  //@dalvik/vm/analysis/DexPrepare
  //优化
  result = dvmOptimizeDexFile(optFd, dexOffset, fileSize, fileName, modTime, adler32, isBootstrap);
  ...
  //@dalvik/vm/DvmDex.cpp
  dvmDexFileOpenFromFd(optFd, &pDvmDex);
}
@dalvik/vm/DvmDex.cpp
int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex){
  DvmDex* pDvmDex;
  DexFile* pDexFile;
  ...
  //重点 1,用以解析 Dex 文件,返回一个对应的 DexFile 数据结构
  //@dalvik/libdex/DexFile.cpp
  pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
  // 重点 2,根据 DexFile 填充 DvmDex 信息
  pDvmDex = allocateAuxStructures(pDexFile);
}

重点 1

//@dalvik/libdex/DexFile.cpp
DexFile* dexFileParse(const u1*data, size_t length, int flags){
  DexFile* pDexFile = NULL;
  const DexHeader* pHeader;
  ...
  //分配内存
  pDexFile = (DexFile*) malloc(sizeof(DexFile));
  ...
  dexParseOptData(data, length, pDexFile);
  ...
  //通过 Dex 文件信息,填充 DexFile 的信息
  dexFileSetupBasicPointers(pDexFile, data);
  ...
}

//data 是打开的 Dex 文件在内存中的地址,而 DexHeader 中的形如 stringIdsOff 都是相对于文件首地址的偏移,因此在 DexFile 中的 pStringIds、pTypeIds 保存的是 Dex 文件中stringIds,TypeIds 等信息在内存中的实际地址。
void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data){
    DexHeader *pHeader = (DexHeader*) data;

    pDexFile->baseAddr = data;
    pDexFile->pHeader = pHeader;
    pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff);
    pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);
    pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff);
    pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->methodIdsOff);
    pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);
    pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff);
    pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff);
}


重点 2
@DvmDex.cpp
//根据 DexFile 的信息填充 DvmDex
static DvmDex* allocateAuxStructres(DexFile* pDexFile){
DvmDex* pDvmDex;
const DexHeader* pHeader;
u4 stringCount, classCount, methodCount, fileCount;

pDvmDex = (DvmDex*) calloc(1, sizeof(DvmDex));
pDvmDex->pDexFile = pDexFile;
pDvmDex->pHeader = pDexFile->pHeader;

pHeader = pDvmDex->pHeader;

stringCount = pHeader->stringIdsSize;
classCount = pHeader->typeIdsSize;
methodCount = pHeader->methodIdsSize;
filedCount = pHeader->fileIdsSize;
//根据实际的数量分配内存
pDvmDex->pResStrings = (struct StringObject) calloc(stringCount, sizeof(struct stringObject));
pDvmDex->pResClasses = (struct ClassObject
) calloc(ClassObject, sizeof(struct ClassObject));
pDvmDex->pResMethods = (struct Method) calloc(methodCount, sizeof(struct Method));
pDvmDex->pResFilds = (struct Field
) calloc(fieldCount, sizeof(struct Field));
...
pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);
}


*从上面看出来,DvmDex 包含 DexFile,DexFile 包含 DexHeader。通过解析 DexHeader,将常量池,方法区,字段等信息在内存的首地址存入 DexFile;然后通过 DexFile 的信息在 DvmDex 中分配合适的内存。于是 Dex 文件便初步解析完成了,并且保存在了内存。在进行类加载的时候,一个类的信息在内存中以 ClassObject 的结构保存。同时在 DexFile 中有一个 ClassLookup* pClassLookup 作为一个哈希表作为缓存保存已经加载了的类。

另外native private static int openDexFile(String sourceName, String outputName, int flags)的返回值是一个 int。明显这个 int 是对应 native 中一个对象的句柄。那么来看 Dalvik_dalvik_system_DexFile_openDexFile()@dalvik_system_DexFile.cpp 中的返回值是 DexOrJar。
于是在 DexFile 中的 cookie 保存的便是这个 DexOrJar 的地址。


https://github.com/Wi1ls/DexParse


3012162-ba700811b814155a.png
opt

转载于:https://www.jianshu.com/p/93a7a036efe0

猜你喜欢

转载自blog.csdn.net/weixin_33749131/article/details/91311535