BruceFan's Blog

Stay hungry, stay foolish

0%

Dalvik类加载模块

类加载机制

Dalvik虚拟机中,类加载机制的主要功能就是将应用程序中Dalvik操作码以及程序数据提取并加载到虚拟机内部。Dex文件是类加载机制的输入文件,输出是一个名为ClassObject的数据结构实例对象。

工作流程

类加载机制的主要内容及工作流程主要分三点:
(1) 对Dex文件进行验证并优化。
(2) 对优化后的Dex文件进行解析。
(3) 对指定类进行实际加载。

Dex文件的优化与验证

为了保证原Dex文件的数据安全与优化机制的独立性,优化机制重新创建一个.Odex文件,主要包括依赖库关系寄存器映射关系以及类的索引关系

Odex文件结构分析

Dex文件与Odex文件结构对比:

DexOptHeader数据结构定义:

1
2
3
4
5
6
7
8
9
10
11
struct DexOptHeader {
u1 magic[8]; //Odex文件版本标识
u4 dexOffset; // 原Dex文件起始位置偏移量(0x28)
u4 dexLenght; // Dex文件总长度
u4 depsOffset; // Odex文件依赖库列表偏移量
u4 depsLength; // 依赖库信息总长度
u4 optOffset; // 优化数据信息偏移量
u4 optLength; // 优化数据总长度,类索引信息封装在这
u4 flags; // 标识位,用于标示Odex优化与验证选项
u4 checksum; // 文件校验和
}
  1. 依赖库信息

Dependence结构不会被加载进内存,Android源码中也没有它的明确定义,以下为整理形式。

1
2
3
4
5
6
7
8
9
10
11
struct Dependence {
u4 modWhen; // 时间戳
u4 crc; // 校验信息
u4 DALVIK_VM_BUILD; // 虚拟机版本号
u4 numDeps; // 依赖库个数
struct {
u4 len; // name长度
u4 name[len]; // 依赖库名称
kSHA1DigestLen signature; // SHA-1值
} table[numDeps];
};
  1. 类索引信息
1
2
3
4
5
6
7
8
9
struct DexClassLookup {
int size; // 总大小
int numEntries; // 表的项数
struct {
u4 classDescriptorHash; // 类描述符的哈希值
int classDescriptorOffset; // Dex文件中该类描述符的偏移位置
int classDefOffset; // Dex文件中该类定义偏移位置
} table[1];
};

numEntries是通过dexRoundUpPower2()(用于求比一个数大的最小的2的整数次幂,如数为6,结果为8,降低了哈希冲突率)函数生成。

函数执行流程

PackageManagerService->Installer->installd->do_dexopt->dexopt->run_dexopt->/system/bin/dexopt
/system/bin/dexopt的代码位于dalvik/dexopt/OptMain.cpp文件,其中extractAndProcessZip()是优化机制的主控程序。
代码清单 dalvik/dexopt/OptMain.cpp: extractAndProcessZip()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
* Extract "classes.dex" from zipFd into "cacheFd", leaving a little space
* up front for the DEX optimization header.
*/
static int extractAndProcessZip(int zipFd, int cacheFd,
const char* debugFileName, bool isBootstrap, const char* bootClassPath,
const char* dexoptFlagStr)
{
ZipArchive zippy; // 用于描述ZIP压缩文件的数据结构
ZipEntry zipEntry; // 用于表示一个ZIP入口
size_t uncompLen;
long modWhen, crc32;
off_t dexOffset; // 用于表示在Odex文件中,原Dex文件的起始地址
int err;
int result = -1;
int dexoptFlags = 0; /* bit flags, from enum DexoptFlags */
// 设置默认的优化模式
DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;

memset(&zippy, 0, sizeof(zippy));
/* make sure we're still at the start of an empty file */
if (lseek(cacheFd, 0, SEEK_END) != 0) {
ALOGE("DexOptZ: new cache file '%s' is not empty", debugFileName);
goto bail;
}
/*
* Write a skeletal DEX optimization header. We want the classes.dex
* to come just after it.
*/
err = dexOptCreateEmptyHeader(cacheFd);
if (err != 0)
goto bail;
/* record the file position so we can get back here later */
// 取得Odex文件中原Dex文件的起始位置,实际就是一个Odex文件头部的长度
dexOffset = lseek(cacheFd, 0, SEEK_CUR);
if (dexOffset < 0)
goto bail;
/*
* Open the zip archive, find the DEX entry.
*/
if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) {
ALOGW("DexOptZ: unable to open zip archive '%s'", debugFileName);
goto bail;
}
// 获取目标Dex文件的解压入口
zipEntry = dexZipFindEntry(&zippy, kClassesDex);
if (zipEntry == NULL) {
ALOGW("DexOptZ: zip archive '%s' does not include %s",
debugFileName, kClassesDex);
goto bail;
}
/*
* Extract some info about the zip entry.
*/
if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL,
&modWhen, &crc32) != 0)
{
ALOGW("DexOptZ: zip archive GetEntryInfo failed on %s", debugFileName);
goto bail;
}
uncompLen = uncompLen;
modWhen = modWhen;
crc32 = crc32;
/*
* Extract the DEX data into the cache file at the current offset.
* 从ZIP文件将目标Dex文件解压出来,并写入cacheFd所指文件,此时cacheFd所指文件非空,
* 包括一个Odex文件头部加上一个原始的Dex文件
*/
if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0) {
ALOGW("DexOptZ: extraction of %s from %s failed",
kClassesDex, debugFileName);
goto bail;
}
/* Parse the options. */
/*
* 入口参数dexoptFlagStr,对验证优化需求进行分析,dexoptFlagStr实际上是一个字符串,
* 记录了验证优化的要求
*/
if (dexoptFlagStr[0] != '\0') {
const char* opc;
const char* val;
// 设置验证模式
opc = strstr(dexoptFlagStr, "v="); /* verification */
if (opc != NULL) {
switch (*(opc+2)) {
case 'n': verifyMode = VERIFY_MODE_NONE; break;
case 'r': verifyMode = VERIFY_MODE_REMOTE; break;
case 'a': verifyMode = VERIFY_MODE_ALL; break;
default: break;
}
}
// 设置优化模式
opc = strstr(dexoptFlagStr, "o="); /* optimization */
if (opc != NULL) {
switch (*(opc+2)) {
case 'n': dexOptMode = OPTIMIZE_MODE_NONE; break;
case 'v': dexOptMode = OPTIMIZE_MODE_VERIFIED; break;
case 'a': dexOptMode = OPTIMIZE_MODE_ALL; break;
case 'f': dexOptMode = OPTIMIZE_MODE_FULL; break;
default: break;
}
}

opc = strstr(dexoptFlagStr, "m=y"); /* register map */
if (opc != NULL) {
dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
}

opc = strstr(dexoptFlagStr, "u="); /* uniprocessor target */
if (opc != NULL) {
switch (*(opc+2)) {
case 'y': dexoptFlags |= DEXOPT_UNIPROCESSOR; break;
case 'n': dexoptFlags |= DEXOPT_SMP; break;
default: break;
}
}
}
/*
* Prep the VM and perform the optimization.
* 完成了原Dex文件的提取以及验证优化选项的设置,即可以开始真正的优化工作,需要
* 初始化一个虚拟机专门用于验证优化工作
*/
if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
dexoptFlags) != 0)
{
ALOGE("DexOptZ: VM init failed");
goto bail;
}
//vmStarted = 1;
/* do the optimization */
if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName,
modWhen, crc32, isBootstrap))
{
ALOGE("Optimization failed");
goto bail;
}
/* we don't shut the VM down -- process is about to exit */
result = 0;
bail:
dexZipCloseArchive(&zippy);
return result;
}

代码清单 dalvik/vm/analysis/DexPrepare.cpp: dvmContinueOptimization()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
{
// 声明相关中间变量
DexClassLookup* pClassLookup = NULL; // 类索引信息
RegisterMapBuilder* pRegMapBuilder = NULL; // 寄存器映射关系信息

assert(gDvm.optimizing);
ALOGV("Continuing optimization (%s, isb=%d)", fileName, isBootstrap);

assert(dexOffset >= 0); // 判断输入文件长度非0
// 对目标文件进行合法性检验,Dex文件长度不能小于其文件头长度
if (dexLength < (int) sizeof(DexHeader)) {
ALOGE("too small to be DEX");
return false;
}
// Odex文件中的Dex文件的起始偏移量不能小于Odex文件头的长度
if (dexOffset < (int) sizeof(DexOptHeader)) {
ALOGE("not enough room for opt header");
return false;
}
...
/*
* 将fd所指文件映射到某一内存位置,该位置的起始地址为mapAddr,其大小
* 就为fd所指文件大小,即一个Odex文件头部加上一个Dex文件长度
*/
mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
if (mapAddr == MAP_FAILED) {
ALOGE("unable to mmap DEX cache: %s", strerror(errno));
goto bail;
}
// 设置相关的验证优化选项
bool doVerify, doOpt;
if (gDvm.classVerifyMode == VERIFY_MODE_NONE) {
doVerify = false;
} else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE) {
doVerify = !gDvm.optimizingBootstrapClass;
} else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/ {
doVerify = true;
}

if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE) {
doOpt = false;
} else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED ||
gDvm.dexOptMode == OPTIMIZE_MODE_FULL) {
doOpt = doVerify;
} else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/ {
doOpt = true;
}
// 重写文件,主要包括:字符顺序调整、结构重新对齐、类验证、字节码优化
success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
doVerify, doOpt, &pClassLookup, NULL);
...
// 对文件进行8字节对齐
off_t depsOffset, optOffset, endOffset, adjOffset;
int depsLength, optLength;
u4 optChecksum;

depsOffset = lseek(fd, 0, SEEK_END); // 取得fd所指文件大小
if (depsOffset < 0) {
ALOGE("lseek to EOF failed: %s", strerror(errno));
goto bail;
}
// depsOffset是dependency list起始地址
adjOffset = (depsOffset + 7) & ~(0x07); // 8字节对齐,adjOffset>=depsOffset
if (adjOffset != depsOffset) {
ALOGV("Adjusting deps start from %d to %d",
(int) depsOffset, (int) adjOffset);
depsOffset = adjOffset;
lseek(fd, depsOffset, SEEK_SET);
}
// 写入依赖库信息
if (writeDependencies(fd, modWhen, crc) != 0) {
ALOGW("Failed writing dependencies");
goto bail;
}
// 计算依赖库长度,调整优化信息的起始地址8字节对齐
optOffset = lseek(fd, 0, SEEK_END);
depsLength = optOffset - depsOffset;

adjOffset = (optOffset + 7) & ~(0x07);
if (adjOffset != optOffset) {
ALOGV("Adjusting opt start from %d to %d",
(int) optOffset, (int) adjOffset);
optOffset = adjOffset;
lseek(fd, optOffset, SEEK_SET);
}
// 写入其他优化信息,包括类索引以及寄存器映射关系
if (!writeOptData(fd, pClassLookup, pRegMapBuilder)) {
ALOGW("Failed writing opt data");
goto bail;
}
...
// 对Odex文件的头部内容进行修正
DexOptHeader optHdr;
memset(&optHdr, 0xff, sizeof(optHdr));
memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);
memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);
optHdr.dexOffset = (u4) dexOffset;
optHdr.dexLength = (u4) dexLength;
optHdr.depsOffset = (u4) depsOffset;
optHdr.depsLength = (u4) depsLength;
optHdr.optOffset = (u4) optOffset;
optHdr.optLength = (u4) optLength;
...
return result;

Dex文件的解析

DexFile数据结构

Dex文件解析的重要目标是为Dex文件生成一个DexFile数据结构。
DexFile结构体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct DexFile {
const DexOptHeader* pOptHeader; // 优化数据头
const DexHeader* pHeader; // Dex文件头
const DexStringId * pStringIds; // 指向字符串索引区
const DexTypeId* pTypeIds; // 指向类型索引区
const DexFieldId* pFieldIds; // 指向字段索引区
const DexMethodId* pMethodIds; // 指向方法索引区
const DexProtoId* pProtoIds; // 指向原型索引区
const DexClassDef* pClassDefs; // 指向类定义区
const DexLink* pLinkData; // 指向链接数据区

const DexClassLookup* pClassLookup; // 类索引
const void* pRegisterMapPool; // 寄存器映射关系
const u1* baseAddr; // 基地址
int overhead;
}

Dex文件解析流程

  1. 对Odex文件进行完整性校验
  2. 解析Odex文件中的优化数据
  3. 为Dex文件与相关数据结构建立映射关系
  4. 为Dex文件进行校验并计算SHA-1
  5. 保存设置并返回

函数执行流程

Dex文件的解析工作主要由虚拟机源码目录dalvik/vm/RawDexFile.cpp中的dvmRawDexFileOpen()函数完成,这部分工作中称其为主控函数。
代码清单 dalvik/vm/RawDexFile.cpp: dvmRawDexFileOpen()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
RawDexFile** ppRawDexFile, bool isBootstrap)
{
// 声明函数中间的执行变量
DvmDex* pDvmDex = NULL; // 用于在虚拟机中描述解析的Dex文件
char* cachedName = NULL; // 用于保存执行期间产生的优化Dex文件名
int result = -1; // 设置函数返回值,0表示成功
int dexFd = -1; // 目标Dex文件的文件描述符
int optFd = -1; // 优化Dex文件的文件描述符
u4 modTime = 0; // 文件修改时间参数
u4 adler32 = 0; // 校验和
size_t fileSize = 0;
bool newFile = false; // 标示虚拟机是否需要对Dex文件进行优化
bool locked = false; // 标示优化进程占用
// fileName记录了Dex文件在文件系统中的绝对路径,用open函数根据fileName将目标文件读入内存
dexFd = open(fileName, O_RDONLY);
if (dexFd < 0) goto bail;
/* If we fork/exec into dexopt, don't let it inherit the open fd. */
dvmSetCloseOnExec(dexFd);
// 对dex文件的合法性与正确性进行检验
if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) {
ALOGE("Error with header for %s", fileName);
goto bail;
}
// 记录文件修改时间并赋值给modTime
if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) {
ALOGE("Error with stat for %s", fileName);
goto bail;
}
// 根据目标Dex文件名为其产生相应的Odex文件名,并赋值给cachedName
if (odexOutputName == NULL) {
cachedName = dexOptGenerateCacheFileName(fileName, NULL);
if (cachedName == NULL)
goto bail;
} else {
cachedName = strdup(odexOutputName);
}

ALOGV("dvmRawDexFileOpen: Checking cache for %s (%s)",
fileName, cachedName);
/*
* 尝试根据cachedName所指的Odex文件名在cache中查找并读取Odex文件,如果读取失败或是当前的Odex文件有误
* 则需要重新对Dex文件进行优化(将newFile设为true)
*/
optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime,
adler32, isBootstrap, &newFile, /*createIfMissing=*/true);

if (optFd < 0) {
ALOGI("Unable to open or create cache for %s (%s)",
fileName, cachedName);
goto bail;
}
locked = true;

// 虚拟机根据newFile的值决定是否对Dex文件进行优化
if (newFile) {
u8 startWhen, copyWhen, endWhen;
bool result;
off_t dexOffset;

dexOffset = lseek(optFd, 0, SEEK_CUR);
result = (dexOffset > 0);

if (result) {
startWhen = dvmGetRelativeTimeUsec();
// 将dexFd所指文件复制到optFd所指文件中
result = copyFileToFile(optFd, dexFd, fileSize) == 0;
copyWhen = dvmGetRelativeTimeUsec();
}
// 调用dvmOptimizeDexFile函数对optFd所指文件进行优化
if (result) {
result = dvmOptimizeDexFile(optFd, dexOffset, fileSize,
fileName, modTime, adler32, isBootstrap);
}

if (!result) {
ALOGE("Unable to extract+optimize DEX from '%s'", fileName);
goto bail;
}

endWhen = dvmGetRelativeTimeUsec();
ALOGD("DEX prep '%s': copy in %dms, rewrite %dms",
fileName,
(int) (copyWhen - startWhen) / 1000,
(int) (endWhen - copyWhen) / 1000);
}

// 当Dex文件的优化结束后,将会调用dvmDexFileOpenFromFd函数对该Dex文件进行解析
if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) {
ALOGI("Unable to map cached %s", fileName);
goto bail;
}
...
ALOGV("Successfully opened '%s'", fileName);
// 对入口参数ppRawDexFile进行设置,其作用是用于保存当前处理的Dex文件的相关信息
*ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
(*ppRawDexFile)->cacheFileName = cachedName; // 保存Odex文件名
(*ppRawDexFile)->pDvmDex = pDvmDex; // 保存DvmDex数据结构
cachedName = NULL; // don't free it below
result = 0;
...
return result;
}

dvmRawDexFileOpen()函数完成目标Dex文件优化后,会调用dvmDexFileOpenFromFd()函数完成Dex文件的后续解析工作。
代码清单 dalvik/vm/DvmDex.cpp: dvmDexFileOpenFromFd()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
{
// 声明函数执行过程中所用到的中间变量
DvmDex* pDvmDex; // 用于在虚拟机中描述解析的Dex文件
DexFile* pDexFile;
MemMapping memMap;
int parseFlags = kDexParseDefault;
int result = -1;
// 验证Dex文件校验和
if (gDvm.verifyDexChecksum)
parseFlags |= kDexParseVerifyChecksum;

if (lseek(fd, 0, SEEK_SET) < 0) {
ALOGE("lseek rewind failed");
goto bail;
}
// 对目标Dex文件进行映射,并将其设置为只读文件
if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
ALOGE("Unable to map file");
goto bail;
}
// Dex文件解析的关键,dexFileParse函数对Dex文件解析,并返回一个DexFile数据结构的实例对象
pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
if (pDexFile == NULL) {
ALOGE("DEX parse failed");
sysReleaseShmem(&memMap);
goto bail;
}
// allocateAuxStructures函数根据pDexFile对DvmDex数据结构的一些成员变量进行设置。
pDvmDex = allocateAuxStructures(pDexFile);
if (pDvmDex == NULL) {
dexFileFree(pDexFile);
sysReleaseShmem(&memMap);
goto bail;
}
/* tuck this into the DexFile so it gets released later */
sysCopyMap(&pDvmDex->memMap, &memMap);
pDvmDex->isMappedReadOnly = true;
*ppDvmDex = pDvmDex;
result = 0; // 0表示成功

bail:
return result;
}

dvmDexFileOpenFromFd()函数首先对已经优化的Dex文件进行正确性检验,随后调用dexFileParse()函数将目标文件进行解析,目标是将Dex文件与DexFile结构体建立关联。
代码清单 dalvik/libdex/DexFile.cpp: dexFileParse()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
DexFile* dexFileParse(const u1* data, size_t length, int flags)
{
DexFile* pDexFile = NULL; // 结构体指针,用于保存和返回解析结果
const DexHeader* pHeader; // 保存Dex文件头部信息
const u1* magic; // 用于保存Dex文件的魔数
int result = -1;
// Dex文件长度不能小于其文件头的长度
if (length < sizeof(DexHeader)) {
ALOGE("too short to be a valid .dex");
goto bail; /* bad file format */
}

pDexFile = (DexFile*) malloc(sizeof(DexFile));
if (pDexFile == NULL)
goto bail; /* alloc failure */
memset(pDexFile, 0, sizeof(DexFile));
// 对目标文件的magic进行验证,确定其为一个优化的Dex文件
if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
magic = data;
if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
ALOGE("bad opt version (0x%02x %02x %02x %02x)",
magic[4], magic[5], magic[6], magic[7]);
goto bail;
}
// 将优化文件头部与DexFile数据结构的pOptHeader成员变量关联
pDexFile->pOptHeader = (const DexOptHeader*) data;
ALOGV("Good opt header, DEX offset is %d, flags=0x%02x",
pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);
// 对优化数据进行处理,将各个优化数据与DexFile数据结构中的相应成员变量进行关联
if (!dexParseOptData(data, length, pDexFile))
goto bail;
// 用data变量记录当前文件所分析到的位置,length记录还有多少内容没分析
data += pDexFile->pOptHeader->dexOffset;
length -= pDexFile->pOptHeader->dexOffset;
if (pDexFile->pOptHeader->dexLength > length) {
ALOGE("File truncated? stored len=%d, rem len=%d",
pDexFile->pOptHeader->dexLength, (int) length);
goto bail;
}
length = pDexFile->pOptHeader->dexLength;
}
// 从data所标示的位置将Dex文件中其他各部分数据与DexFile数据结构建立完整的映射关系
dexFileSetupBasicPointers(pDexFile, data);
pHeader = pDexFile->pHeader;

if (!dexHasValidMagic(pHeader)) {
goto bail;
}
// 验证Dex文件校验和
if (flags & kDexParseVerifyChecksum) {
...
}
// 验证SHA-1值
if (kVerifySignature) {
...
}
...
result = 0;
return pDexFile;
}

随着dexFileParse函数结束,Dex文件的解析也告一段落,类加载机制接下来的工作就是根据虚拟机的运行需要,从Dex文件中加载指定类,并将其装入虚拟机的运行时环境中。

运行时环境数据加载

ClassObject数据结构

类加载的最终目标就是为目标类生成一个ClassObject的实例对象,并将其存储在运行时环境中,随时被执行模块引用执行。
代码清单 dalvik/vm/oo/Object.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
struct ClassObject : Object {
u4 instanceData[CLASS_FIELD_SLOTS];
const char* descriptor; // 类描述符
char* descriptorAlloc;
u4 accessFlags; // 访问标志符
u4 serialNumber; // 虚拟机独有的类序列号
DvmDex* pDvmDex; // 指向所属Dex文件
ClassStatus status; // 类初始化状态
ClassObject* verifyErrorClass; // 错误处理
u4 initThreadId; // 初始化进程ID
size_t objectSize; // 总共的object数
ClassObject* elementClass; // 元素类
int arrayDim; // 数组维数
PrimitiveType primitiveType; // 原始类型索引
ClassObject* super; // 指向超类
Object* classLoader; // 类装载器
InitiatingLoaderList initiatingLoaderList;
/* 这个类中直接实现的接口的数组 */
int interfaceCount;
ClassObject** interfaces;
/* 直接方法 */
int directMethodCount;
Method* directMethods;
/* 这个类中定义的虚方法,通过vtable调用 */
int virtualMethodCount;
Method* virtualMethods;
/* 虚方法表 */
int vtableCount;
Method** vtable;
/* interface table */
int iftableCount;
InterfaceEntry* iftable;
/* 常量池 */
int ifviPoolCount;
int* ifviPool;
/* 实例字段 */
int ifieldCount;
int ifieldRefCount;
InstField* ifields;

u4 refOffsets; // 字段区偏移量
const char* sourceFile; // 源文件名
/* 静态字段 */
int sfieldCount;
StaticField sfields[0];
}

类加载整体流程

  • 获取描述Dex文件的DexFile结构体对象
  • 根据目标类属性选择相应的加载模式
  • 在DexFile数据结构中获取目标类数据在Dex文件中的分布信息
  • 将类数据信息传给实际加载函数

函数执行流程

dalvik/vm/native/dalvik_system_DexFile.cpp中的Dalvik_dalvik_System_DexFile_defineClassNative为这一阶段的主控函数。
Dalvik_dalvik_system_DexFile_defineClassNative->dvmGetRawDexFileDex->dvmDefineClass->findClassNoInit完成实际的加载工作。
代码清单 dalvik/vm/oo/Class.cpp: findClassNoInit()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
DvmDex* pDvmDex)
{
Thread* self = dvmThreadSelf();
ClassObject* clazz; // 类加载的最终形式
bool profilerNotified = false;
/* 判断目标类是否有类加载器,对于系统类,虚拟机将从默认的启动路径实现其加载工作
对于用户类,虚拟机一般情况下使用默认的类加载器实现类加载工作 */
if (loader != NULL) {
LOGVV("#### findClassNoInit(%s,%p,%p)", descriptor, loader,
pDvmDex->pDexFile);
}
...
/* 根据目标类的描述符从hash表(系统已加载类)里查找是否已经有该Class的信息,如果已经加载,则返回
其ClassObject对象,否则,对目标类进行加载*/
clazz = dvmLookupClass(descriptor, loader, true);
if (clazz == NULL) {
const DexClassDef* pClassDef;

dvmMethodTraceClassPrepBegin();
profilerNotified = true;

#if LOG_CLASS_LOADING
u8 startTime = dvmGetThreadCpuTimeNsec();
#endif
/* 判断是否存在DvmDex结构体对象,如果存在,则表示目标类为一个用户类,将从一个解析的Dex文件
中进行加载,对于一个解析过的Dex文件,是一定存在一个DvmDex结构体对象的,故pDvmDex一定不为空
若为空,则表示目标类是一个系统类,虚拟机将调用searchBootPathForClass函数从启动路径下查找并
加载目标类 */
if (pDvmDex == NULL) {
assert(loader == NULL); /* shouldn't be here otherwise */
/* 从BOOTCLASSPATH里那一堆jar包文件中,看看哪个jar包声明了目标类返回的是一个打开了的代
表odex文件的DvmDex对象 */
pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
} else {
// 查找目标类的类定义资源
pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
}
...
/* 当获得了加载目标类所需的各项资源,主函数将调用loadClassFromDex函数对目标类进行加载 */
clazz = loadClassFromDex(pDvmDex, pClassDef, loader);
if (dvmCheckException(self)) {
/* class was found but had issues */
if (clazz != NULL) {
dvmFreeClassInnards(clazz);
dvmReleaseTrackedAlloc((Object*) clazz, NULL);
}
goto bail;
}
/* 将目前使用的类锁住,防止其他进程更改 */
dvmLockObject(self, (Object*) clazz);
clazz->initThreadId = self->threadId;

assert(clazz->classLoader == loader);
if (!dvmAddClassToHash(clazz)) { // class对象加到Hash表里
clazz->initThreadId = 0;
dvmUnlockObject(self, (Object*) clazz);

/* Let the GC free the class. */
dvmFreeClassInnards(clazz);
dvmReleaseTrackedAlloc((Object*) clazz, NULL);
/* 从已加载的类的系统Hash表中重新得到类 */
clazz = dvmLookupClass(descriptor, loader, true);
assert(clazz != NULL);
goto got_class;
}
...
/*
* 准备开始连接类,对clazz进行一些处理:
* 1.解析ClassObject对象的基类信息,和它实现了那些接口
* 2.校验:比如父类是final的,那么就不应该有它的派生类等
* 此函数调用成功后,clazz的状态将是CLASS_RESOLVED或CLASS_VERIFIED
*/
if (!dvmLinkClass(clazz)) {
assert(dvmCheckException(self));
/* Make note of the error and clean up the class. */
removeClassFromHash(clazz);
clazz->status = CLASS_ERROR;
dvmFreeClassInnards(clazz);
/* Let any waiters know. */
clazz->initThreadId = 0;
dvmObjectNotifyAll(self, (Object*) clazz);
dvmUnlockObject(self, (Object*) clazz);
...
}
/* 将类的状态增加到全局变量中去 */
gDvm.numLoadedClasses++;
gDvm.numDeclaredMethods +=
clazz->virtualMethodCount + clazz->directMethodCount;
gDvm.numDeclaredInstFields += clazz->ifieldCount;
gDvm.numDeclaredStaticFields += clazz->sfieldCount;
...
/* check some invariants */
assert(dvmIsClassLinked(clazz));
assert(gDvm.classJavaLangClass != NULL);
assert(clazz->clazz == gDvm.classJavaLangClass);
assert(dvmIsClassObject(clazz));
assert(clazz == gDvm.classJavaLangObject || clazz->super != NULL);
if (!dvmIsInterfaceClass(clazz)) {
//ALOGI("class=%s vtableCount=%d, virtualMeth=%d",
// clazz->descriptor, clazz->vtableCount,
// clazz->virtualMethodCount);
assert(clazz->vtableCount >= clazz->virtualMethodCount);
}

bail:
if (profilerNotified)
dvmMethodTraceClassPrepEnd();
assert(clazz != NULL || dvmCheckException(self));
return clazz;
}

调用dvmLookupClass函数判断本类是否已经被加载

  • 已加载:直接使用,结束函数。
  • 未加载:判断能否找到Dex文件,能找到调用dexFindClass在指定Dex文件中根据类的描述符查找相关类(用户类);找不到调用searchBootPathForClass从系统启动基本路径中查找并加载目标类(系统类)。调用loadClassFromDex函数实现加载类达到可运行状态。调用dvmAddClassToHash实现将新加载的类添加到哈希表中方便在此查找。

findClassNoInit函数将调用loadClassFromDex0函数完成对该类的加载工作,返回值为一个ClassObject结构体对象。loadClassFromDex0函数源代码如下:
代码清单 dalvik/vm/oo/Class.cpp: loadClassFromDex0()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
static ClassObject* loadClassFromDex0(DvmDex* pDvmDex,
const DexClassDef* pClassDef, const DexClassDataHeader* pHeader,
const u1* pEncodedData, Object* classLoader)
{
ClassObject* newClass = NULL; // 目标类的类实例对象
const DexFile* pDexFile; // 用于存储目标Dex文件所对应的DexFile数据结构实例对象
const char* descriptor; // 用于存储目标类的描述符
int i;
// 获取相应的类信息
pDexFile = pDvmDex->pDexFile;
descriptor = dexGetClassDescriptor(pDexFile, pClassDef);
...
// 为即将生成的类对象实例申请内存空间
assert(descriptor != NULL);
// 判断是不是java.lang.Class类,此类已经初始化过了
if (classLoader == NULL &&
strcmp(descriptor, "Ljava/lang/Class;") == 0) {
assert(gDvm.classJavaLangClass != NULL);
newClass = gDvm.classJavaLangClass;
} else {
// 取得对象实例大小并在内存中申请相应内存
size_t size = classObjectSize(pHeader->staticFieldsSize);
newClass = (ClassObject*) dvmMalloc(size, ALLOC_NON_MOVING);
}
if (newClass == NULL)
return NULL;

// 对新的类对象实例进行初始化
DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
dvmSetClassSerialNumber(newClass);
newClass->descriptor = descriptor;
assert(newClass->descriptorAlloc == NULL);
SET_CLASS_FLAG(newClass, pClassDef->accessFlags);
// 设定字段对象
dvmSetFieldObject((Object *)newClass,
OFFSETOF_MEMBER(ClassObject, classLoader),
(Object *)classLoader);
// 设定类的相关指针
newClass->pDvmDex = pDvmDex;
newClass->primitiveType = PRIM_NOT;
newClass->status = CLASS_IDX;
// 将这个类的父类的索引加入到类对象的指针区域
assert(sizeof(u4) == sizeof(ClassObject*)); /* 32-bit check */
newClass->super = (ClassObject*) pClassDef->superclassIdx;

const DexTypeList* pInterfacesList;
// 得到接口列表
pInterfacesList = dexGetInterfacesList(pDexFile, pClassDef);
if (pInterfacesList != NULL) {
newClass->interfaceCount = pInterfacesList->size;
newClass->interfaces = (ClassObject**) dvmLinearAlloc(classLoader,
newClass->interfaceCount * sizeof(ClassObject*));
// newClass实现了哪些接口类,此处也先以接口类的index存储,后续放到dvmLinkClass来解析
for (i = 0; i < newClass->interfaceCount; i++) {
const DexTypeItem* pType = dexGetTypeItem(pInterfacesList, i);
newClass->interfaces[i] = (ClassObject*)(u4) pType->typeIdx;
}
dvmLinearReadOnly(classLoader, newClass->interfaces);
}
// 对字段进行加载,首先加载静态字段
if (pHeader->staticFieldsSize != 0) {
/* static fields stay on system heap; field data isn't "write once" */
int count = (int) pHeader->staticFieldsSize;
u4 lastIndex = 0;
DexField field;
// 取得字段数
newClass->sfieldCount = count;
// 逐一加载字段
for (i = 0; i < count; i++) {
dexReadClassDataField(&pEncodedData, &field, &lastIndex);
// 解析newClass定义的静态成员信息
loadSFieldFromDex(newClass, &field, &newClass->sfields[i]);
}
}
// 加载实例字段
if (pHeader->instanceFieldsSize != 0) {
int count = (int) pHeader->instanceFieldsSize;
u4 lastIndex = 0;
DexField field;
// 取得字段数
newClass->ifieldCount = count;
newClass->ifields = (InstField*) dvmLinearAlloc(classLoader,
count * sizeof(InstField));
// 逐一加载字段
for (i = 0; i < count; i++) {
dexReadClassDataField(&pEncodedData, &field, &lastIndex);
loadIFieldFromDex(newClass, &field, &newClass->ifields[i]);
}
dvmLinearReadOnly(classLoader, newClass->ifields);
}
// 对类方法进行加载
if (pHeader->directMethodsSize != 0) {
int count = (int) pHeader->directMethodsSize;
u4 lastIndex = 0;
DexMethod method;
// 取得方法数目
newClass->directMethodCount = count;
newClass->directMethods = (Method*) dvmLinearAlloc(classLoader,
count * sizeof(Method));
// 逐一加载方法
for (i = 0; i < count; i++) {
dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
loadMethodFromDex(newClass, &method, &newClass->directMethods[i]);
if (classMapData != NULL) {
const RegisterMap* pMap = dvmRegisterMapGetNext(&classMapData);
if (dvmRegisterMapGetFormat(pMap) != kRegMapFormatNone) {
newClass->directMethods[i].registerMap = pMap;
/* TODO: add rigorous checks */
assert((newClass->directMethods[i].registersSize+7) / 8 ==
newClass->directMethods[i].registerMap->regWidth);
}
}
}
dvmLinearReadOnly(classLoader, newClass->directMethods);
}
// 加载虚方法
if (pHeader->virtualMethodsSize != 0) {
int count = (int) pHeader->virtualMethodsSize;
u4 lastIndex = 0;
DexMethod method;
// 取得虚方法数目
newClass->virtualMethodCount = count;
newClass->virtualMethods = (Method*) dvmLinearAlloc(classLoader,
count * sizeof(Method));
// 逐一处理方法
for (i = 0; i < count; i++) {
dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
loadMethodFromDex(newClass, &method, &newClass->virtualMethods[i]);
if (classMapData != NULL) {
const RegisterMap* pMap = dvmRegisterMapGetNext(&classMapData);
if (dvmRegisterMapGetFormat(pMap) != kRegMapFormatNone) {
newClass->virtualMethods[i].registerMap = pMap;
/* TODO: add rigorous checks */
assert((newClass->virtualMethods[i].registersSize+7) / 8 ==
newClass->virtualMethods[i].registerMap->regWidth);
}
}
}
dvmLinearReadOnly(classLoader, newClass->virtualMethods);
}
// 保存源文件信息
newClass->sourceFile = dexGetSourceFile(pDexFile, pClassDef);

/* caller must call dvmReleaseTrackedAlloc */
return newClass; // 返回类对象
}

依次完成:1.在内存中为类对象申请存储空间;2.设置字段信息;3.为超类建立索引;4.加载类接口;5.加载类字段;6.加载类方法,并将以上数据封装成一个ClassObject结构体对象并返回。