0x01 前言
学习一下 Android 系统关于 dex 文件加载的过程,已经有很多前辈分享过无数的文章了,我只在这里做一个简单的记录,有助于自己查阅和学习 ART 虚拟机。这里分析使用的代码时基于 4.2.2 版本的系统。
0x02 一些必要的结构体
在 dex 文件加载过程中 java 中的类其实涉及并不多,主要还是集中在 native 层
下面就简单罗列一下(建议提前熟悉一下 DEX 文件结构):
// DexOrJar 结构体 主要是表示一个已经加载好的 dex 一些基本信息
struct DexOrJar {
char* fileName; // 文件名
bool isDex; // 是 dex 还是 jar (也有可能是一个 jar 文件)
bool okayToFree;
RawDexFile* pRawDexFile;
JarFile* pJarFile; // 如果是jar包,则指向JarFile结构
u1* pDexMemory; // 如果是dex文件,则指向dex文件存放的内存区 };
struct RawDexFile {
29 char* cacheFileName;
30 DvmDex* pDvmDex;
31};
32
struct DvmDex {
38 /* pointer to the DexFile we're associated with */
39 DexFile* pDexFile;
40
41 /* clone of pDexFile->pHeader (it's used frequently enough) */
42 const DexHeader* pHeader;
43
44 /* interned strings; parallel to "stringIds" */
45 struct StringObject** pResStrings;
46
47 /* resolved classes; parallel to "typeIds" */
48 struct ClassObject** pResClasses;
49
50 /* resolved methods; parallel to "methodIds" */
51 struct Method** pResMethods;
52
53 /* resolved instance fields; parallel to "fieldIds" */
54 /* (this holds both InstField and StaticField) */
55 struct Field** pResFields;
56
57 /* interface method lookup cache */
58 struct AtomicCache* pInterfaceCache;
59
60 /* shared memory region with file contents */
61 bool isMappedReadOnly;
62 MemMapping memMap;
63
64 /* lock ensuring mutual exclusion during updates */
65 pthread_mutex_t modLock;
66};
0x003 正文
一般的类加载 都会用 DexClassloader,所以从入口开始看
1. DexClassLoader
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
}
没干别的,就是将四个参数传给了父类构造函数
2. BaseDexClassLoader
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
public BaseDexClassLoader(
String dexPath,
File optimizedDirectory,
String libraryPath,
ClassLoader parent
) {
super(parent);
this.originalPath = dexPath;
this.originalLibraryPath = libraryPath;
this.pathList =
new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
}
在构造函数里将传入参数保存一下,然后就是调用 DexPathList 的构造函数创建对象
3. DexPathList
/*package*/ final class DexPathList {
/** class definition context */
private final ClassLoader definingContext;
/** list of dex/resource (class path) elements */
private final Element[] dexElements;
/** list of native library directory elements */
private final File[] nativeLibraryDirectories;
public DexPathList(
ClassLoader definingContext,
String dexPath,
String libraryPath,
File optimizedDirectory) {
if (definingContext == null) {
throw new NullPointerException("definingContext == null");
}
if (dexPath == null) {
throw new NullPointerException("dexPath == null");
}
if (optimizedDirectory != null) {
if (!optimizedDirectory.exists()) {
throw new IllegalArgumentException(
"optimizedDirectory doesn't exist: "
+ optimizedDirectory);
}
if (!(optimizedDirectory.canRead()
&& optimizedDirectory.canWrite())) {
throw new IllegalArgumentException(
"optimizedDirectory not readable/writable: "
+ optimizedDirectory);
}
}
this.definingContext = definingContext;
this.dexElements =
makeDexElements(splitDexPath(dexPath), optimizedDirectory);
this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
}
这个函数现实检查一下传入参数中 definingContext、dexPath、optimizedDirectory 都是不是空
然后调用 makeDexElements 函数,创建一个 Element 类的数组
private static void splitAndAdd(String path, boolean wantDirectories,
158 ArrayList<File> resultList) {
159 if (path == null) {
160 return;
161 }
162
163 String[] strings = path.split(Pattern.quote(File.pathSeparator));
164
165 for (String s : strings) {
166 File file = new File(s);
167
168 if (! (file.exists() && file.canRead())) {
169 continue;
170 }
171
172 /*
173 * Note: There are other entities in filesystems than
174 * regular files and directories.
175 */
176 if (wantDirectories) {
177 if (!file.isDirectory()) {
178 continue;
179 }
180 } else {
181 if (!file.isFile()) {
182 continue;
183 }
184 }
185
186 resultList.add(file);
187 }
188 }
189
splitDexPath(dexPath)最终会掉用到上面这个函数,就是将传入路径下的文件都放到一个 ArrayList 中。
private static Element[] makeDexElements(ArrayList<File> files,
195 File optimizedDirectory) {
196 ArrayList<Element> elements = new ArrayList<Element>();
197
198 /*
199 * Open all files and load the (direct or contained) dex files
200 * up front.
201 */
202 for (File file : files) {
203 File zip = null;
204 DexFile dex = null;
205 String name = file.getName();
206
207 if (name.endsWith(DEX_SUFFIX)) {
208 // Raw dex file (not inside a zip/jar).
209 try {
210 dex = loadDexFile(file, optimizedDirectory);
211 } catch (IOException ex) {
212 System.logE("Unable to load dex file: " + file, ex);
213 }
214 } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
215 || name.endsWith(ZIP_SUFFIX)) {
216 zip = file;
217
218 try {
219 dex = loadDexFile(file, optimizedDirectory);
220 } catch (IOException ignored) {
221 /*
222 * IOException might get thrown "legitimately" by
223 * the DexFile constructor if the zip file turns
224 * out to be resource-only (that is, no
225 * classes.dex file in it). Safe to just ignore
226 * the exception here, and let dex == null.
227 */
228 }
229 } else {
230 System.logW("Unknown file type for: " + file);
231 }
232
233 if ((zip != null) || (dex != null)) {
234 elements.add(new Element(file, zip, dex));
235 }
236 }
237
238 return elements.toArray(new Element[elements.size()]);
239 }
makeDexElements 根据文件的类型调用 loadDexFile 函数,生成一个 DexFile 对象,并将它发到数组中保存。
private static DexFile loadDexFile(File file, File optimizedDirectory)
246 throws IOException {
247 if (optimizedDirectory == null) {
248 return new DexFile(file);
249 } else {
250 String optimizedPath = optimizedPathFor(file, optimizedDirectory);
251 return DexFile.loadDex(file.getPath(), optimizedPath, 0);
252 }
253 }
loadDexFile 调用 DexFile 构造函数,或者 DexFile.loadDex 生成一个 DexFile 对象。
4. DexFile
其实 DexFile.loadDex 最终也是调用 DexFile 构造函数
static public DexFile loadDex(String sourcePathName, String outputPathName,
140 int flags) throws IOException {
149 return new DexFile(sourcePathName, outputPathName, flags);
150 }
public DexFile(String fileName) throws IOException {
78 mCookie = openDexFile(fileName, null, 0);
79 mFileName = fileName;
80 guard.open("close");
81 //System.out.println("DEX FILE cookie is " + mCookie);
82 }
native private static int openDexFile(String sourceName, String outputName,
273 int flags) throws IOException;
在 DexFile 构造函数中调用了 openDexFile ,并将结果保存在 int 类型的 mCookie 字段中, openDexFile 是一个 native 函数。
5. OpenDexFileNative
代码路径在:/dalvik/vm/native/dalvik_system_DexFile.cpp
151static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args,
152 JValue* pResult)
153{
154 StringObject* sourceNameObj = (StringObject*) args[0];
155 StringObject* outputNameObj = (StringObject*) args[1];
156 DexOrJar* pDexOrJar = NULL;
157 JarFile* pJarFile;
158 RawDexFile* pRawDexFile;
159 char* sourceName;
160 char* outputName;
202 ........
203 /*
204 * Try to open it directly as a DEX if the name ends with ".dex".
205 * If that fails (or isn't tried in the first place), try it as a
206 * Zip with a "classes.dex" inside.
207 */
208 if (hasDexExtension(sourceName)
209 && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
210 ALOGV("Opening DEX file '%s' (DEX)", sourceName);
211
212 pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
213 pDexOrJar->isDex = true;
214 pDexOrJar->pRawDexFile = pRawDexFile;
215 pDexOrJar->pDexMemory = NULL;
216 } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
217 ALOGV("Opening DEX file '%s' (Jar)", sourceName);
218
219 pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
220 pDexOrJar->isDex = false;
221 pDexOrJar->pJarFile = pJarFile;
222 pDexOrJar->pDexMemory = NULL;
223 } else {
224 ALOGV("Unable to open DEX file '%s'", sourceName);
225 dvmThrowIOException("unable to open DEX file");
226 }
227
228 if (pDexOrJar != NULL) {
229 pDexOrJar->fileName = sourceName;
230 addToDexFileTable(pDexOrJar);
231 } else {
232 free(sourceName);
233 }
234
235 RETURN_PTR(pDexOrJar);
236}
首先是一些检查,紧接着调用 dvmRawDexFileOpen 尝试打开 dex 文件,如果失败就会使用 dvmJarFileOpen 尝试打开一个 jar 文件,将结果保存在一个 pDexOrJar 结构中,最后将这个结构体的指针返回给 java 层保存。
这里只分析 dex 的情况,其实 jar 也是差不多的。
dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false)
这个函数有点长,一段一段分析吧
int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
110 RawDexFile** ppRawDexFile, bool isBootstrap)
111{
112 /*
113 * TODO: This duplicates a lot of code from dvmJarFileOpen() in
114 * JarFile.c. This should be refactored.
115 */
116
117 DvmDex* pDvmDex = NULL;
118 char* cachedName = NULL;
119 int result = -1;
120 int dexFd = -1;
121 int optFd = -1;
122 u4 modTime = 0;
123 u4 adler32 = 0;
124 size_t fileSize = 0;
125 bool newFile = false;
126 bool locked = false;
127
128 dexFd = open(fileName, O_RDONLY);
129 if (dexFd < 0) goto bail;
130
131 /* If we fork/exec into dexopt, don't let it inherit the open fd. */
132 dvmSetCloseOnExec(dexFd);
133
134 if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) {
135 ALOGE("Error with header for %s", fileName);
136 goto bail;
137 }
138
139 if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) {
140 ALOGE("Error with stat for %s", fileName);
141 goto bail;
142 }
143
首先使用 open 函数打开 dex 文件, 然后做一些魔数、adler32值、时间和文件大小的检查,并且 adler32 和文件大小保存下来,后面要用。
if (odexOutputName == NULL) {
151 cachedName = dexOptGenerateCacheFileName(fileName, NULL);
152 if (cachedName == NULL)
153 goto bail;
154 } else {
155 cachedName = strdup(odexOutputName);
156 }
157
158 ALOGV("dvmRawDexFileOpen: Checking cache for %s (%s)",
159 fileName, cachedName);
160
161 optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime,
162 adler32, isBootstrap, &newFile, /*createIfMissing=*/true);
163
164 if (optFd < 0) {
165 ALOGI("Unable to open or create cache for %s (%s)",
166 fileName, cachedName);
167 goto bail;
168 }
169 locked = true;
这段代码主要功能就是,为 dex 优化准备一个路径,并创建一个空文件
dexOptGenerateCacheFileName 是用于字符串拼接,拼接一个缓存文件的路径, 然后看看 dvmOpenCachedDexFile
int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
130 u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
131{
132 int fd, cc;
133 struct stat fdStat, fileStat;
134 bool readOnly = false;
135
136 *pNewFile = false;
137
138retry:
139 /*
140 * Try to open the cache file. If we've been asked to,
141 * create it if it doesn't exist.
142 */
143 fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
// 一些容错检查
168 ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
169 cc = flock(fd, LOCK_EX | LOCK_NB);
// 一些获取文件锁 容错检查
189 cc = fstat(fd, &fdStat);
195 cc = stat(cacheFileName, &fileStat);
214 if (fdStat.st_size == 0) {
215 if (readOnly) {
216 ALOGW("DexOpt: file has zero length and isn't writable");
217 goto close_fail;
218 }
219 cc = dexOptCreateEmptyHeader(fd);
220 if (cc != 0)
221 goto close_fail;
222 *pNewFile = true;
223 ALOGV("DexOpt: successfully initialized new cache file");
224 } else {
225 ......
247 if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
248 expectVerify, expectOpt))
249 {
250 if (readOnly) {
257 if (createIfMissing) {
258 ALOGW("Cached DEX '%s' (%s) is stale and not writable",
259 fileName, cacheFileName);
260 }
261 goto close_fail;
262 }
285 ALOGD("ODEX file is stale or bad; removing and retrying (%s)",
286 cacheFileName);
287 if (ftruncate(fd, 0) != 0) {
288 ALOGW("Warning: unable to truncate cache file '%s': %s",
289 cacheFileName, strerror(errno));
290 /* keep going */
291 }
292 if (unlink(cacheFileName) != 0) {
293 ALOGW("Warning: unable to remove cache file '%s': %d %s",
294 cacheFileName, errno, strerror(errno));
295 /* keep going; permission failure should probably be fatal */
296 }
297 LOGVV("DexOpt: unlocking cache file %s", cacheFileName);
298 flock(fd, LOCK_UN);
299 close(fd);
300 goto retry;
301 } else {
302 ALOGV("DexOpt: good deps in cache file");
303 }
304 }
305
306 assert(fd >= 0);
307 return fd;
308
309close_fail:
310 flock(fd, LOCK_UN);
311 close(fd);
312 return -1;
313}
这个函数比较长我删除一些错误处理代码,首先调用 open 打开文件,打开的时候如果没有就会创建一个,并用 fclock 函数获取文件锁,防止文件被其他进程操作, 之后再使用 fstat 获取文件状态,比较一下 cacheFileName 和使用 open 函数打开的那个文件是不是同一个文件。 检查 fdStat.st_size == 0,说明这个文件是当前进程创建的,之后会调用 dexOptCreateEmptyHeade 在缓存文件中构造一个空的 OptHeadser 结构,将 pNewFile 变量设置为 true 说明 这是一个新文件,将文件描述符返回。
现在继续往下执行,从 dvmOpenCachedDexFile 返回后, 有了一个缓存(Opt)文件,并且OptHeader 已经创建并写入到了文件开始的部分。
if (newFile) {
178 u8 startWhen, copyWhen, endWhen;
179 bool result;
180 off_t dexOffset;
181
182 dexOffset = lseek(optFd, 0, SEEK_CUR);
183 result = (dexOffset > 0);
184
185 if (result) {
186 startWhen = dvmGetRelativeTimeUsec();
187 result = copyFileToFile(optFd, dexFd, fileSize) == 0;
188 copyWhen = dvmGetRelativeTimeUsec();
189 }
190
191 if (result) {
192 result = dvmOptimizeDexFile(optFd, dexOffset, fileSize,
193 fileName, modTime, adler32, isBootstrap);
194 }
195
196 if (!result) {
197 ALOGE("Unable to extract+optimize DEX from '%s'", fileName);
198 goto bail;
199 }
200
201 endWhen = dvmGetRelativeTimeUsec();
202 ALOGD("DEX prep '%s': copy in %dms, rewrite %dms",
203 fileName,
204 (int) (copyWhen - startWhen) / 1000,
205 (int) (endWhen - copyWhen) / 1000);
206 }
因为刚才在将建缓存文件时将 newFile 设置为了 true,这里执行括号里面的代码。开始是一些文件操作, 将 dex 文件的内容复制到缓存文件中,位置是紧挨着 OptHeader。 然后调用 dvmOptimizeDexFile, 这个函数就是调用 /bin/dexopt 对 dex 文件进行优化。(这里就不分析了) 继续向下执行
if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) {
213 ALOGI("Unable to map cached %s", fileName);
214 goto bail;
215 }
216
217 if (locked) {
218 /* unlock the fd */
219 if (!dvmUnlockCachedDexFile(optFd)) {
220 /* uh oh -- this process needs to exit or we'll wedge the system */
221 ALOGE("Unable to unlock DEX file");
222 goto bail;
223 }
224 locked = false;
225 }
226
227 ALOGV("Successfully opened '%s'", fileName);
228
229 *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
230 (*ppRawDexFile)->cacheFileName = cachedName;
231 (*ppRawDexFile)->pDvmDex = pDvmDex;
232 cachedName = NULL; // don't free it below
233 result = 0;
dvmRawDexFileOpen 函数调用的最后, 就是构造一个 DvmDex 结构,然后保存在参数 ppRawDexFile 的 RawDexFile 结构中返回给上层。
看一下 dvmDexFileOpenFromFd 是如何构造一个 DvmDex 结构的。
int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
92{
93 DvmDex* pDvmDex;
94 DexFile* pDexFile;
95 MemMapping memMap;
96 int parseFlags = kDexParseDefault;
97 int result = -1;
98
99 if (gDvm.verifyDexChecksum)
100 parseFlags |= kDexParseVerifyChecksum;
101
102 if (lseek(fd, 0, SEEK_SET) < 0) {
103 ALOGE("lseek rewind failed");
104 goto bail;
105 }
106
107 if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
108 ALOGE("Unable to map file");
109 goto bail;
110 }
111
112 pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
113 if (pDexFile == NULL) {
114 ALOGE("DEX parse failed");
115 sysReleaseShmem(&memMap);
116 goto bail;
117 }
118
119 pDvmDex = allocateAuxStructures(pDexFile);
120 if (pDvmDex == NULL) {
121 dexFileFree(pDexFile);
122 sysReleaseShmem(&memMap);
123 goto bail;
124 }
125
126 /* tuck this into the DexFile so it gets released later */
127 sysCopyMap(&pDvmDex->memMap, &memMap);
128 pDvmDex->isMappedReadOnly = true;
129 *ppDvmDex = pDvmDex;
130 result = 0;
131
132bail:
133 return result;
134}
首先将文件映射到内存中,然后使用 dexFileParse 解析 dex 文件,最后填充 DvmDex 字段返回给调用者。
最后再看看 dexFileParse 是怎么实现的
289DexFile* dexFileParse(const u1* data, size_t length, int flags)
290{
291 DexFile* pDexFile = NULL;
292 const DexHeader* pHeader;
293 const u1* magic;
294 int result = -1;
295
296 if (length < sizeof(DexHeader)) {
297 ALOGE("too short to be a valid .dex");
298 goto bail; /* bad file format */
299 }
300
301 pDexFile = (DexFile*) malloc(sizeof(DexFile));
302 if (pDexFile == NULL)
303 goto bail; /* alloc failure */
304 memset(pDexFile, 0, sizeof(DexFile));
305
306 /*
307 * Peel off the optimized header.
308 */
309 if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
310 magic = data;
311 if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
312 ALOGE("bad opt version (0x%02x %02x %02x %02x)",
313 magic[4], magic[5], magic[6], magic[7]);
314 goto bail;
315 }
316
317 pDexFile->pOptHeader = (const DexOptHeader*) data;
318 ALOGV("Good opt header, DEX offset is %d, flags=0x%02x",
319 pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);
320
321 /* parse the optimized dex file tables */
322 if (!dexParseOptData(data, length, pDexFile))
323 goto bail;
324
325 /* ignore the opt header and appended data from here on out */
326 data += pDexFile->pOptHeader->dexOffset;
327 length -= pDexFile->pOptHeader->dexOffset;
328 if (pDexFile->pOptHeader->dexLength > length) {
329 ALOGE("File truncated? stored len=%d, rem len=%d",
330 pDexFile->pOptHeader->dexLength, (int) length);
331 goto bail;
332 }
333 length = pDexFile->pOptHeader->dexLength;
334 }
335
336 dexFileSetupBasicPointers(pDexFile, data);
337 pHeader = pDexFile->pHeader;
338
339 if (!dexHasValidMagic(pHeader)) {
340 goto bail;
341 }
342
343 /*
344 * Verify the checksum(s). This is reasonably quick, but does require
345 * touching every byte in the DEX file. The base checksum changes after
346 * byte-swapping and DEX optimization.
347 */
348 if (flags & kDexParseVerifyChecksum) {
349 u4 adler = dexComputeChecksum(pHeader);
350 if (adler != pHeader->checksum) {
351 ALOGE("ERROR: bad checksum (%08x vs %08x)",
352 adler, pHeader->checksum);
353 if (!(flags & kDexParseContinueOnError))
354 goto bail;
355 } else {
356 ALOGV("+++ adler32 checksum (%08x) verified", adler);
357 }
358
359 const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
360 if (pOptHeader != NULL) {
361 adler = dexComputeOptChecksum(pOptHeader);
362 if (adler != pOptHeader->checksum) {
363 ALOGE("ERROR: bad opt checksum (%08x vs %08x)",
364 adler, pOptHeader->checksum);
365 if (!(flags & kDexParseContinueOnError))
366 goto bail;
367 } else {
368 ALOGV("+++ adler32 opt checksum (%08x) verified", adler);
369 }
370 }
371 }
372
373 /*
374 * Verify the SHA-1 digest. (Normally we don't want to do this --
375 * the digest is used to uniquely identify the original DEX file, and
376 * can't be computed for verification after the DEX is byte-swapped
377 * and optimized.)
378 */
379 if (kVerifySignature) {
380 unsigned char sha1Digest[kSHA1DigestLen];
381 const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +
382 kSHA1DigestLen;
383
384 dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);
385 if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {
386 char tmpBuf1[kSHA1DigestOutputLen];
387 char tmpBuf2[kSHA1DigestOutputLen];
388 ALOGE("ERROR: bad SHA1 digest (%s vs %s)",
389 dexSHA1DigestToStr(sha1Digest, tmpBuf1),
390 dexSHA1DigestToStr(pHeader->signature, tmpBuf2));
391 if (!(flags & kDexParseContinueOnError))
392 goto bail;
393 } else {
394 ALOGV("+++ sha1 digest verified");
395 }
396 }
397
398 if (pHeader->fileSize != length) {
399 ALOGE("ERROR: stored file size (%d) != expected (%d)",
400 (int) pHeader->fileSize, (int) length);
401 if (!(flags & kDexParseContinueOnError))
402 goto bail;
403 }
404
405 if (pHeader->classDefsSize == 0) {
406 ALOGE("ERROR: DEX file has no classes in it, failing");
407 goto bail;
408 }
409
410 /*
411 * Success!
412 */
413 result = 0;
414
415bail:
416 if (result != 0 && pDexFile != NULL) {
417 dexFileFree(pDexFile);
418 pDexFile = NULL;
419 }
420 return pDexFile;
421}
函数很长,但不复杂。就是根据 DEX 文件结构解析文件,并填充相应字段。
行了,基本上主要的流程都分析了,这里简单的回顾一下流程。
DexCLassLoader -> BaseDexClassLoader -> DexPathList -> DexFile -> OpenDexFileNative -> dvmRawDexFileOpen (这里就是优化、解析 Dex 文件)