dex文件打开

dex文件打开我们知道,要读取一个类代码,或读取类里的方法代码,都需要打开Dex文件,然后按前面介绍的格式去分析,并且读取出相应的内容,才可以给虚拟机进行解释执行。现在,我们就来学习和分析Dex文件的读取相关的代码。如下:/**Openthespecifiedfileread-only.Wememory-maptheentirethingand*parsethecontents

大家好,又见面了,我是你们的朋友全栈君。

我们知道,要读取一个类代码,或读取类里的方法代码,都需要打开Dex文件,然后按前面介绍的格式去分析,并且读取出相应的内容,才可以给虚拟机进行解释执行。现在,我们就来学习和分析Dex文件的读取相关的代码。如下:

/*

*Open the specified file read-only. We memory-map the entire thingand

*parse the contents.

*

*This will be called on non-Zip files, especially during VM startup,so

*we don’t want to be too noisy about certain types of failure. (Do

*we want a “quiet” flag?)

*

*On success, we fill out the contents of “pArchive” andreturn 0.

*/

intdexZipOpenArchive(const char* fileName, ZipArchive* pArchive)

{

上面这段代码输入文件名称,输出文件内容对象。

int fd, err;

LOGV(“Opening archive ‘%s’ %p\n”,fileName, pArchive);

memset(pArchive, 0, sizeof(ZipArchive));

上面这行代码清空Dex文件对象。

fd = open(fileName, O_RDONLY, 0);

if (fd < 0) {

err = errno ? errno : -1;

LOGV(“Unable to open ‘%s’: %s\n”,fileName, strerror(err));

return err;

}

这段代码调用函数open来打开文件,如果不成功就输出出错提示。

return dexZipPrepArchive(fd, fileName,pArchive);

这行代码调用函数dexZipPrepArchive来生成dex文件对象。

}

在上面这个函数里,主要输入两个参数:fileNamepArchive。其中fileName是输入要打开的dex文件名称,当然它是包括文件路径的;pArchive是打开这个文件后用来表达dex文件内容的对象。代码具体过程是先调用函数memset来清空pArchive文件对象,然后调用函数open打开文件,把文件句柄传送给函数dexZipPrepArchive

行文件分析,并把相应内容设置给文件对象pArchive

下面来看一下pArchive对象的结构,如下:

typedefstruct ZipArchive {

/* open Zip archive */

int mFd;

这个zip文件的文件句柄,也就是上面调用open函数打开成功后句柄。

/* mapped file */

MemMapping mMap;

这个是把zip文件映射到内存,加快文件读取,提高文件读取效率。

/* number of entries in the Zip archive */

int mNumEntries;

这个是保存zip文件的入口。

/*

* We know how many entries are in the Ziparchive, so we can have a

* fixed-size hash table. We probe oncollisions.

*/

int mHashTableSize;

ZipHashEntry* mHashTable;

这里是通过hash的方法来提高读取文件速度。

}ZipArchive;

ZipArchive结构保存zip文件的句柄、文件内容映射内存地址、zip入口个数和入口地址(使用hash表达)。

这一段代码实现打开Dex文件,由于Dex文件采用zip压缩,所以需要先从zip文件里解压出来,才可以恢复到Dex原始数据。

下面来分析这个函数代码,如下:

intdexZipPrepArchive(int fd, const char* debugFileName, ZipArchive*pArchive)

{

这个函数输入文件句柄、文件名称、压缩文件对象。

MemMapping map;

int err;

map.addr = NULL;

memset(pArchive, 0, sizeof(*pArchive));

pArchive->mFd = fd;

这行代码是保存文件句柄。

if (sysMapFileInShmem(pArchive->mFd,&map) != 0) {

err = -1;

LOGW(“Map of ‘%s’ failed\n”,debugFileName);

goto bail;

}

if (map.length < kEOCDLen) {

err = -1;

LOGV(“File ‘%s’ too small to be zip(%zd)\n”, debugFileName,map.length);

goto bail;

}

这段代码映射文件数据到内存。

if (!parseZipArchive(pArchive, &map)) {

err = -1;

LOGV(“Parsing ‘%s’ failed\n”,debugFileName);

goto bail;

}

这段代码是分析zip文件。

/* success */

err = 0;

sysCopyMap(&pArchive->mMap, &map);

map.addr = NULL;

这段代码拷贝到映射位置。

bail:

if (err != 0)

dexZipCloseArchive(pArchive);

if (map.addr != NULL)

sysReleaseShmem(&map);

return err;

}

函数dexZipPrepArchive的处理,主要就是先保存文件句柄,然后创建文件内存映射,调用parseZipArchive函数来分析zip的所有入口点,并记录到相应的hash表里,最后调用sysCopyMap函数来保存到zip文件对象结构里。

由上面分析可知,dex文件是压缩成zip文件,这样可以减少占用空间。dex文件在系统里是怎么样打开的过程呢?其它经过下面的过程:

1)系统初始化虚拟机时,会初始化原始方法gDvmNativeMethodSet集合。

2)在原始方法集合里有一个函数集合dvm_dalvik_system_DexFile,注册它为Ldalvik/system/DexFile串,当虚拟机调用DexFile相关函数时,就会调用这些函数来处理Dex文件。

3)在处理Dex文件时,会调用函数集合:dvm_dalvik_system_DexFile,这个函数集合里,主要有如下函数:

constDalvikNativeMethod dvm_dalvik_system_DexFile[] = {

{“openDexFile”, “(Ljava/lang/String;Ljava/lang/String;I)I”,

Dalvik_dalvik_system_DexFile_openDexFile},

{“closeDexFile”, “(I)V”,

Dalvik_dalvik_system_DexFile_closeDexFile},

{“defineClass”, “(Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/security/ProtectionDomain;)Ljava/lang/Class;”,

Dalvik_dalvik_system_DexFile_defineClass},

{“getClassNameList”, “(I)[Ljava/lang/String;”,

Dalvik_dalvik_system_DexFile_getClassNameList},

{“isDexOptNeeded”, “(Ljava/lang/String;)Z”,

Dalvik_dalvik_system_DexFile_isDexOptNeeded},

{NULL, NULL, NULL },

};

openDexFile方法对应的原始函数是Dalvik_dalvik_system_DexFile_openDexFile,它是打开Dex文件函数。

closeDexFile方法对应的原始函数是Dalvik_dalvik_system_DexFile_closeDexFile,它是关闭已经打开的Dex文件函数。

4)在Dalvik_dalvik_system_DexFile_openDexFile函数里,调用函数dvmJarFileOpen打开JAR或者ZIP压缩的文件。

5)在dvmJarFileOpen函数里,调用dexZipOpenArchive来处理ZIP文件,调用dexZipFindEntry函数读取ZIP解压的文件,调用dvmDexFileOpenFromFd函数读取相应的类数据到内存,并返回给虚拟机。

从上面可知调用函数Dalvik_dalvik_system_DexFile_openDexFile来打开Dex文件,这个函数的源码如下:

staticvoid Dalvik_dalvik_system_DexFile_openDexFile(const u4* args,

JValue* pResult)

{

StringObject* sourceNameObj =(StringObject*) args[0];

这行是输入的Jar或Dex文件名参数。

StringObject* outputNameObj =(StringObject*) args[1];

这行是输出的文件名参数。

int flags = args[2];

这行是处理的标示。

DexOrJar* pDexOrJar = NULL;

JarFile* pJarFile;

RawDexFile* pRawDexFile;

char* sourceName;

char* outputName;

if (sourceNameObj == NULL) {

dvmThrowException(“Ljava/lang/NullPointerException;”,NULL);

RETURN_VOID();

}

这段代码是当输入文件名称为空对象时,就抛出异常。

sourceName =dvmCreateCstrFromString(sourceNameObj);

这行代码调用函数dvmCreateCstrFromStringjava字符串转换C字符串,由于Java对象表示的字符串并不能立即就使用到C语言里,所以需要转换才能使用。

if (outputNameObj != NULL)

outputName =dvmCreateCstrFromString(outputNameObj);

else

outputName = NULL;

这段代码是同样把输出字符串转换C字符串。

/*

* We have to deal with the possibility thatsomebody might try to

* open one of our bootstrap class DEXfiles. The set of dependencies

* will be different, and hence the resultsof optimization might be

* different, which means we’d actually needto have two versions of

* the optimized DEX: one that only knowsabout part of the boot class

* path, and one that knows about everythingin it. The latter might

* optimize field/method accesses based on aclass that appeared later

* in the class path.

*

* We can’t let the user-defined classloader open it and start using

* the classes, since the optimized form ofthe code skips some of

* the method and field resolution that wewould ordinarily do, and

* we’d have the wrong semantics.

*

* We have to reject attempts to manuallyopen a DEX file from the boot

* class path. The easiest way to do thisis by filename, which works

* out because variations in name (e.g.”/system/framework/./ext.jar”)

* result in us hitting a differentdalvik-cache entry. It’s also fine

* if the caller specifies their own outputfile.

*/

if(dvmClassPathContains(gDvm.bootClassPath, sourceName)) {

LOGW(“Refusing to reopen boot DEX’%s’\n”, sourceName);

dvmThrowException(“Ljava/io/IOException;”,

“Re-opening BOOTCLASSPATH DEXfiles is not allowed”);

free(sourceName);

RETURN_VOID();

}

这段代码是判断用户是否加载系统目录下面的Dex文件,如果加载就要拒绝这样的操作,因为系统启动时已经加载了一份这样的优化代码,没有必要再次加载一次。

/*

* Try to open it directly as a DEX. Ifthat fails, try it as a Zip

* with a “classes.dex” inside.

*/

if (dvmRawDexFileOpen(sourceName,outputName, &pRawDexFile, false) == 0) {

LOGV(“Opening DEX file ‘%s'(DEX)\n”, sourceName);

pDexOrJar = (DexOrJar*)malloc(sizeof(DexOrJar));

pDexOrJar->isDex = true;

pDexOrJar->pRawDexFile = pRawDexFile;

这段代码是尝试加载Dex文件,但基本不存在直接加Dex文件的情况,因此在函数dvmRawDexFileOpen还是空函数,没有实际的内容。

}else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false)== 0) {

LOGV(“Opening DEX file ‘%s'(Jar)\n”, sourceName);

pDexOrJar = (DexOrJar*)malloc(sizeof(DexOrJar));

pDexOrJar->isDex = false;

pDexOrJar->pJarFile = pJarFile;

这段代码是加载Jar文件,就是从这里加载Dex文件到缓存里。

}else {

LOGV(“Unable to open DEX file’%s’\n”, sourceName);

dvmThrowException(“Ljava/io/IOException;”,”unable to open DEX file”);

}

if (pDexOrJar != NULL) {

pDexOrJar->fileName = sourceName;

这行代码保存文件名称到Dex文件对象里。

/* add to hash table */

u4 hash = dvmComputeUtf8Hash(sourceName);

这行代码通过文件名称计算HASH串,加速对文件的查找速度。

void* result;

dvmHashTableLock(gDvm.userDexFiles);

result =dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,

hashcmpDexOrJar, true);

dvmHashTableUnlock(gDvm.userDexFiles);

这段代码添加HASH表里,以便后面查找使用。

if (result != pDexOrJar) {

LOGE(“Pointer has already beenadded?\n”);

dvmAbort();

}

pDexOrJar->okayToFree = true;

}else

free(sourceName);

RETURN_PTR(pDexOrJar);

这行代码返回打开的文件对象。

}

这个函数是通过JAVA调用时输入Dex文件名称,然后加载Dex文件,最后把这个文件名称放到HASH表里,然后返回打开的对象。

在上面的函数里,提到使用dvmJarFileOpen函数找到classes.dex文件,并加载到内存里,然后提供后面的函数使用。现在就来分析这个函数的代码,如下:

intdvmJarFileOpen(const char* fileName, const char* odexOutputName,

JarFile** ppJarFile, bool isBootstrap)

{

在这里提供四个参数,第一个参数fileName是输入的Jar的文件名称;第二个参数odexOutputName是进行优化后的Dex输出文件;第三个参数ppJarFile是已经打开并缓存到内存里的文件对象;第四个参数isBootstrap是指示是否系统里Dex文件。

ZipArchive archive;

DvmDex* pDvmDex = NULL;

char* cachedName = NULL;

bool archiveOpen = false;

bool locked = false;

int fd = -1;

int result = -1;

/* Even if we’re not going to look at thearchive, we need to

* open it so we can stuff it intoppJarFile.

*/

if (dexZipOpenArchive(fileName, &archive)!= 0)

goto bail;

archiveOpen = true;

这段代码是调用前面介绍的函数dexZipOpenArchive来打开Zip文件,并缓存到系统内存里。

/* If we fork/exec into dexopt, don’t letit inherit the archive’s fd.

*/

dvmSetCloseOnExec(dexZipGetArchiveFd(&archive));

这行代码设置当执行完成后,关闭这个文件句柄。

/* First, look for a “.odex”alongside the jar file. It will

* have the same name/path except for theextension.

*/

fd = openAlternateSuffix(fileName, “odex”,O_RDONLY, &cachedName);

if (fd >= 0) {

这里优先处理优化的Dex文件。

LOGV(“Using alternate file (odex)for %s …\n”, fileName);

if (!dvmCheckOptHeaderAndDependencies(fd,false, 0, 0, true, true)) {

LOGE(“%s odex has staledependencies\n”, fileName);

free(cachedName);

close(fd);

fd = -1;

goto tryArchive;

} else {

LOGV(“%s odex has gooddependencies\n”, fileName);

//TODO: make sure that the .odexactually corresponds

// to the classes.dex inside thearchive (if present).

// For typical use there will beno classes.dex.

}

}else {

ZipEntry entry;

tryArchive:

/*

* Pre-created .odex absent or stale. Look inside the jar for a

* “classes.dex”.

*/

entry = dexZipFindEntry(&archive,kDexInJarName);

这行代码是从压缩包里找到Dex文件,然后打开这个文件。

if (entry != NULL) {

bool newFile = false;

/*

* We’ve found the one we want. Seeif there’s an up-to-date copy

* in the cache.

*

* On return, “fd” will beseeked just past the “opt” header.

*

* If a stale .odex file is presentand classes.dex exists in

* the archive, this will *not*return an fd pointing to the

* .odex file; the fd will point intodalvik-cache like any

* other jar.

*/

if (odexOutputName == NULL) {

cachedName =dexOptGenerateCacheFileName(fileName,

kDexInJarName);

if (cachedName == NULL)

goto bail;

这段代码是把Dex文件进行优化处理,并输出到指定的文件。

} else {

cachedName =strdup(odexOutputName);

}

LOGV(“dvmDexCacheStatus:Checking cache for %s (%s)\n”,

fileName, cachedName);

fd = dvmOpenCachedDexFile(fileName,cachedName,

dexGetZipEntryModTime(&archive,entry),

dexGetZipEntryCrc32(&archive,entry),

isBootstrap, &newFile,/*createIfMissing=*/true);

这段代码创建缓存的优化文件。

if (fd < 0) {

LOGI(“Unable to open orcreate cache for %s (%s)\n”,

fileName, cachedName);

goto bail;

}

locked = true;

/*

* If fd points to a new file(because there was no cached version,

* or the cached version was stale),generate the optimized DEX.

* The file descriptor returned isstill locked, and is positioned

* just past the optimization header.

*/

if (newFile) {

u8 startWhen, extractWhen,endWhen;

bool result;

off_t dexOffset, fileLen;

dexOffset = lseek(fd, 0,SEEK_CUR);

result = (dexOffset > 0);

if (result) {

startWhen =dvmGetRelativeTimeUsec();

result =dexZipExtractEntryToFile(&archive, entry, fd);

extractWhen =dvmGetRelativeTimeUsec();

这段代码调用函数dexZipExtractEntryToFile从压缩包里解压文件出来。

}

if (result) {

result =dvmOptimizeDexFile(fd, dexOffset,

dexGetZipEntryUncompLen(&archive,entry),

fileName,

dexGetZipEntryModTime(&archive,entry),

dexGetZipEntryCrc32(&archive,entry),

isBootstrap);

这段代码调用函数dvmOptimizeDexFile对Dex文件进行优化处理。

}

if (!result) {

LOGE(“Unable toextract+optimize DEX from ‘%s’\n”,

fileName);

goto bail;

}

endWhen =dvmGetRelativeTimeUsec();

LOGD(“DEX prep ‘%s’: unzipin %dms, rewrite %dms\n”,

fileName,

(int) (extractWhen -startWhen) / 1000,

(int) (endWhen – extractWhen)/ 1000);

}

} else {

LOGI(“Zip is good, but no %sinside, and no valid .odex “

“file in the samedirectory\n”, kDexInJarName);

goto bail;

}

}

/*

* Map the cached version. This immediatelyrewinds the fd, so it

* doesn’t have to be seeked anywhere inparticular.

*/

if (dvmDexFileOpenFromFd(fd, &pDvmDex)!= 0) {

LOGI(“Unable to map %s in %s\n”,kDexInJarName, fileName);

goto bail;

}

这段代码是调用函数dvmDexFileOpenFromFd来缓存Dex文件,并分析文件的内容。比如标记是否优化的文件,通过签名检查Dex文件是否合法。

if (locked) {

/* unlock the fd */

if (!dvmUnlockCachedDexFile(fd)) {

/* uh oh — this process needs toexit or we’ll wedge the system */

LOGE(“Unable to unlock DEXfile\n”);

goto bail;

}

locked = false;

}

这段代码是保存文件到缓存里,标记这个文件句柄已经保存到缓存。

LOGV(“Successfully opened ‘%s’ in’%s’\n”, kDexInJarName, fileName);

*ppJarFile = (JarFile*) calloc(1,sizeof(JarFile));

(*ppJarFile)->archive = archive;

(*ppJarFile)->cacheFileName =cachedName;

(*ppJarFile)->pDvmDex = pDvmDex;

cachedName = NULL; // don’t free itbelow

result = 0;

这段代码已经成功打开压缩包,并读取Dex文件到内存缓存,这里只是设置一些相关信息返回前面的函数处理。

bail:

/* clean up, closing the open file */

if (archiveOpen && result != 0)

dexZipCloseArchive(&archive);

free(cachedName);

if (fd >= 0) {

if (locked)

(void) dvmUnlockCachedDexFile(fd);

close(fd);

}

return result;

}

本函数主要流程是打开压缩文件,然后找到Dex文件,读取出来,并进行分析,然后拷贝到缓存里,再返回给调用函数

 
 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/153520.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 两周搞定计算机专业毕业设计,附源码+论文+答辩

    两周搞定计算机专业毕业设计,附源码+论文+答辩毕设+论文+答辩通关法则,看这一篇,源码都在下面了!写在前面,尽管论文方法和毕设源码都有,但还是要看课+自己敲完完善好。做好充分准备面对答辩。文章目录一、毕设二、论文2.1论文标题2.2确定论文大纲2.3论文内容2.3.1文科找文献:2.3.2论文摘要2.3.3绪论2.3.4论文内容2.4论文查重三、答辩3.1论文答辩前的准备3.2论文答辩现场对弈一、毕设毕设直通车《黑马智慧物业毕业设计》【毕设项目】01网上购物商城(前端+后端+云部署)【毕设项目】02网上购物商城(前端

  • 新手小白学电脑_新手小白开公司

    新手小白学电脑_新手小白开公司1set接口1.1 概述Set是一个不包含重复数据的CollectionSet集合中的数据是无序的(因为Set集合没有下标)Set集合中的元素不可以重复–常用来给数据去重1.2 Set集合的特点数据无序且数据不允许重复HashSet:底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复。TreeSet:底层是TreeMap,也是红黑树的形式,便于查找数据1.3 常用方法学习Collecti

  • Spring boot + Spring Security 多种登录认证方式配置(一)

    Spring boot + Spring Security 多种登录认证方式配置(一)

  • 概率中的PDF,PMF,CDF

    概率中的PDF,PMF,CDF一概念解释二数学表示三概念分析四分布函数的意义五参考文献一.概念解释PDF:概率密度函数(probabilitydensityfunction),在数学中,连续型随机变量的概率密度函数(在不至于混淆时可以简称为密度函数)是一个描述这个随机变量的输出值,在某个确定的取值点附近的可能性的函数。PMF:概率质量函数(probabilitymassfunction),在概率

  • python自动化运维面试题_运维面试题(含答案)「建议收藏」

    python自动化运维面试题_运维面试题(含答案)「建议收藏」运维工程师面试题姓名:答题时间:1.新安装MYSQL后怎样提升MYSQL的安全级别?A.修改mysql默认端口B.linux下可以通过iptables来限制访问mysql端口的IP地址C.对所有用户设置较复杂密码并严格指定对应账号的访问IP(可在mysql库中user表中指定用户的访问可访问IP地址)D.root特权账号的处理(建议给root账号设置强密码,并指定只允许本地登录)E.开启二进制查询…

  • 设备树传参中的platform device的匹配

    设备树传参中的platform device的匹配在高版本的Linux内核中使用了设备树进行传参,之前购买一块nanopi的板子使用的是linux4.11.2版本的内核(使用的友善之臂的Mainlinelinux)就是这种情况,并且使用设备树传参过后,原来硬编码在mach-xxx.c文件中的platformdevice全部都放入了设备树中,而原来使用name进行platformdevice和driver进行匹配的方式也发生了变化。以

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号