Android类加载器

Android脱壳一个关键点就是DexClassLoader,于是好好看了下类加载器~

java代码首次运行会被编译为.class文件,每个类为一个单独的文件(包括内部类也是有单独类文件),Java虚拟机会读入这些.class文件并将其解析为对应的类,这是由类加载器来实现的,一个Java程序在运行过程中不止一个类加载器,一个类加载器可以加载多各类,每个被解析了的类一定都有加载它的类加载器。

Java类加载器

每个类有自己的类加载器,用户也可以自己定义自己的类加载器,同一个类文件被不同加载器加载时,他们就不是同一个类了,使用isInstance()等将会得到False,如下:

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
package test;

import java.io.IOException;
import java.io.InputStream;

public class Test {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
ClassLoader myClassLoader = new ClassLoader() { //新建一个类加载器对象
@Override //重写loadClass方法
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);//从classpath下当前类目录寻找文件并作为输入流
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length); //解析类
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
};
System.out.println(test.Test.class.getName());
System.out.println(test.Test.class.getClassLoader());

System.out.println();

Object obj = myClassLoader.loadClass("test.Test").newInstance();
System.out.println(obj.getClass().getName());
System.out.println(obj.getClass().getClassLoader());

System.out.println(obj instanceof test.Test);
}
}

运行结果:

1
2
3
4
5
6
test.Test
sun.misc.Launcher$AppClassLoader@6d06d69c

test.Test
test.Test$1@33909752
false

可以看到同一个类可以被不同的类加载器加载,Java有四种不同的类加载器:

  1. 启动类加载器(Bootstrap ClassLoader):代码中就不能改它了,在程序启动时通过参数的方式可以指定,如burpsuite的破解版用到的:java -Xbootclasspath/p:burp-loader-keygen.jar -jar burpsuite_pro_v1.7.31.jar
  2. 扩展类加载器(Extension ClassLoader):应用程序员能用到的最顶层的类加载器,它的父加载器为空,它用于加载java.ext.dir /lib/ext等目录的类
  3. 应用程序类加载器(Application ClassLoader):也叫系统类加载器,可由getSystemClassLoader()返回,用户代码的类默认有其加载。
  4. 用户自定义类加载器:用户自己实现的

其实后三者基本相同,只是一般由双亲委派模式来为其分层级,想象一下:

  1. 为了管理与查找的方便,所有类加载器应该用链表将他们连接起来
  2. 当一个类已经被某个类加载器加载以后,一般没必要再用其他加载器再次加载它,也没必要在同一个类加载器里面重复加载它

Java的类加载器间逻辑关系如下,组成了一个树形结构,每个类加载器都有一个parent属性指向其父加载器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
                     +------------------------------------------------+
| Bootstrap ClassLoader |
+--------------------+XX+------------------------+
XXXXXX
X+
+--------------------+X+-------------------------+
| Extension ClassLoader |
+---------------------+XX+-----------------------+
XXXXXX
X
+---------------------+X+------------------------+
| Application ClassLoader |
+----+XX+-------------------------+XX+-----------+
XXXXX XXXXXX
X X
+-----------------------+ +-+X+-------------------+
| 用户自定义类加载器1 | | 用户自定义类加载器3 |
+--+XX+---------+XX+----+ +-----------------------+
XXXXXXX XXX+XXX
+ |
+-----------------+------+ +-+---------------------+
| 用户自定义类加载器2 | | 用户自定义类加载器4 |
+------------------------+ +---------------------+

于是下面这种代码实现将是一个不错的选择:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//当加载一个类时:
protected synchronized Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{
//check the class has been loaded or not
Class c = findLoadedClass(name); //先在当前类加载器中查找,若找到表明已加载过直接返回该类即可
if(c == null){
try{
if(parent != null){
c = parent.loadClass(name,false); //若存在父加载器,在父加载器里面做同样的操作
}else{
c = findBootstrapClassOrNull(name); //若不存在父加载器了,那就说明这是扩展类加载器了,此时在启动类加载器中查找
}
}catch(ClassNotFoundException e){
//if throws the exception ,the father can not complete the load
}
if(c == null){
c = findClass(name); //此时在所有上层类加载器的已加载类中都没有找到,就自己去加载它
}
}
if(resolve){
resolveClass(c);
}
return c;
}

这样就实现了上面所要求的功能,使同一个类不会被加载两次,可是回顾上面说明isInstance()时的代码,test.Test类明明就被两个不同的类加载器调用了鸭!仔细观察其实可以发现两种条件下同一个类会被多次加载:

  1. 多个类加载器可以有同一个父加载器,于是就出现了分支,实际上他是一个树形结构,每个类加载器为一个节点,根据以上代码,加载类时它只会从指定类加载器(节点)向扩展类加载器(或者启动类加载器)(根)查找,并不会对树进行遍历,所以不在查找路径上就会再次被加载。
  2. 又看发现上面的逻辑实现是在loadClass里,只要重写它就可能破坏这种逻辑,也就是isInstance()那个例子处的情况,所以为了保留这种逻辑一般都是重写findClass

Android类加载器

上面介绍的为一个类对应一个文件的情况,实际上类加载器的输入可以jar zip等各种格式,Android的二进制文件被整体存放在Dex文件里,那么Android的类加载输入也一般是Dex文件,那么直观上看一次加载的就不是一个类而是Dex文件里的所有类了,显然对这些文件的加载类应该是上面提到的加载器的封装。
实际上dalvik在类加载部分分两个主要步骤:

  1. 将指定的Dex文件加载到内存中
  2. 在需要时从内存中的DexFile中加载相应的类,完成创建与链接操作
  3. 次要部分:在触发条件时对类对象进行初始化

注意:本篇的源码基于Android 4.4并且做了大量的删减(如断言校验等都被删除),请根据需要选择是否继续阅读

阅读源码,DexClassLoader等类加载器类最终都是BaseDexClassLoader类的子类,于是从它开始分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList; //重点部分

/* 步骤一:*/
public BaseDexClassLoader(String dexPath, //要加载的dex的地址,可以有多个,将会加载该目录下所有dex,多个路径用分隔符连接
File optimizedDirectory, //优化后的dex文件输出目录,可以为空
String libraryPath, //要用到的native库的地址,多个地址用分隔符如:连接
ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}
/* 步骤二:*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = pathList.findClass(name, suppressedExceptions); //实际调用的pathList.findClass方法加载类
}
}

如注释,在构造器中就实现了文件加载到内存,并在findClass中完成指定类的加载,下面详细分析。

Dex2DexFile

DexPathList

1
2
3
4
5
6
7
8
9
10
public DexPathList(ClassLoader definingContext,         //即为新创建的这个类加载器自己
String dexPath, //原样传入
String libraryPath,
File optimizedDirectory) {

this.definingContext = definingContext;
this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, //主要部分
suppressedExceptions);
this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
}

makeDexElements

它的第一个参数顾名思义就是把一个由多个地址组成的字符串分割成一个ArrayList,它有多个文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static Element[] makeDexElements(ArrayList<File> files,              //指定的Dex文件路径下的所有文件
File optimizedDirectory, //原样
ArrayList<IOException> suppressedExceptions) {
ArrayList<Element> elements = new ArrayList<Element>();
for (File file : files) {
File zip = null;
DexFile dex = null;
String name = file.getName();

if (name.endsWith(DEX_SUFFIX)) { //对以".dex"结尾的文件
dex = loadDexFile(file, optimizedDirectory);
} else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX) //对以".zip .jar .apk"这种zip格式的压缩文件
|| name.endsWith(ZIP_SUFFIX)) {
zip = file;
dex = loadDexFile(file, optimizedDirectory);
}
} else if ...

if ((zip != null) || (dex != null)) {
elements.add(new Element(file, false, zip, dex));
}
}
return elements.toArray(new Element[elements.size()]);
}

loadDexFile

1
2
3
4
5
6
7
8
9
10
private static DexFile loadDexFile(File file,       //Dex Or Zip 文件
File optimizedDirectory) //原样
throws IOException {
if (optimizedDirectory == null) {
return new DexFile(file); //直接新建
} else {
String optimizedPath = optimizedPathFor(file, optimizedDirectory);//获取优化后的文件路径(文件以.dex结尾)
return DexFile.loadDex(file.getPath(), optimizedPath, 0);
}
}

DexFile

看到optimizedDirectory是否为空对应着两种分支,跟入分支:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*分支一:*/
public DexFile(String fileName) throws IOException {
mCookie = openDexFile(fileName, null, 0); //mCookic在后面会知道时pDexOrJar
mFileName = fileName;
guard.open("close");
}
/*分之二:*/
static public DexFile loadDex(String sourcePathName, //原文件 dex Or Zip
String outputPathName, //优化后的文件名
int flags) throws IOException { //0

return new DexFile(sourcePathName, outputPathName, flags);
}
private DexFile(String sourceName, String outputName, int flags) throws IOException {
mCookie = openDexFile(sourceName, outputName, flags);
mFileName = sourceName;
guard.open("close");
//System.out.println("DEX FILE cookie is " + mCookie);
}

openDexFile

看到两种分支最后都会调用openDexFile,它们的第三个参数flags都为0,只有optimizedDirectory不同,那么分支可以合并继续分析:

1
2
3
4
5
6
7
8
private static int openDexFile(String sourceName, String outputName,
int flags) throws IOException {
return openDexFileNative(new File(sourceName).getCanonicalPath(),
(outputName == null) ? null : new File(outputName).getCanonicalPath(),
flags);
}
native private static int openDexFileNative(String sourceName, String outputName,
int flags) throws IOException;

Dalvik_dalvik_system_DexFile_openDexFileNative

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
{ "openDexFileNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
Dalvik_dalvik_system_DexFile_openDexFileNative },
{ "openDexFile", "([B)I",
Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
{ "closeDexFile", "(I)V",
Dalvik_dalvik_system_DexFile_closeDexFile },
{ "defineClassNative", "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;",
Dalvik_dalvik_system_DexFile_defineClassNative },
{ "getClassNameList", "(I)[Ljava/lang/String;",
Dalvik_dalvik_system_DexFile_getClassNameList },
{ "isDexOptNeeded", "(Ljava/lang/String;)Z",
Dalvik_dalvik_system_DexFile_isDexOptNeeded },
{ NULL, NULL, NULL },
};
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
 /*三个参数:
String sourceName, //dex Or Zip
String outputName, //输出文件
int flags //0
*/
static void Dalvik_dalvik_system_DexFile_openDexFileNative(const u4* args,JValue* pResult)
{
StringObject* sourceNameObj = (StringObject*) args[0];
StringObject* outputNameObj = (StringObject*) args[1];
DexOrJar* pDexOrJar = NULL;
JarFile* pJarFile;
RawDexFile* pRawDexFile;
char* sourceName;
char* outputName;

sourceName = dvmCreateCstrFromString(sourceNameObj); //把Java字符串转化为C字符串
if (outputNameObj != NULL)
outputName = dvmCreateCstrFromString(outputNameObj);
else
outputName = NULL;

if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) { } //禁止手动载入boot类

if (hasDexExtension(sourceName)
&& dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
ALOGV("Opening DEX file '%s' (DEX)", sourceName);

pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); //对.dex和zip类文件进行两种不同的加载
pDexOrJar->isDex = true;
pDexOrJar->pRawDexFile = pRawDexFile;
pDexOrJar->pDexMemory = NULL;
} else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
ALOGV("Opening DEX file '%s' (Jar)", sourceName);

pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
pDexOrJar->isDex = false;
pDexOrJar->pJarFile = pJarFile;
pDexOrJar->pDexMemory = NULL;
} else {
ALOGV("Unable to open DEX file '%s'", sourceName);
dvmThrowIOException("unable to open DEX file");
}

if (pDexOrJar != NULL) {
pDexOrJar->fileName = sourceName;
addToDexFileTable(pDexOrJar); //将装载的pDexOrZip地址添加到userDexFiles这个hash表里
} else {
free(sourceName);
}

free(outputName);
RETURN_PTR(pDexOrJar);
}

涉及到的数据结构DexOrJar:

1
2
3
4
5
6
7
8
struct DexOrJar {
char* fileName;
bool isDex;
bool okayToFree;
RawDexFile* pRawDexFile;
JarFile* pJarFile;
u1* pDexMemory; // malloc()ed memory, if any
};

dvmRawDexFileOpen

可以看到对.dex.zip .jar .apk文件的读取使用了两种不同的函数,但是对后者在对其进行解压等操作后会回到前者这种情况,于是直接从对.dex处理这种情况分析:

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
int dvmRawDexFileOpen(const char* fileName,         //原Dex文件路径
const char* odexOutputName, //输出路径
RawDexFile** ppRawDexFile, //被加载到内存的位置,为 "输出属性" 参数
bool isBootstrap) //false
{
DvmDex* pDvmDex = NULL;
char* cachedName = NULL;
int result = -1;
int dexFd = -1;
int optFd = -1;
u4 modTime = 0;
u4 adler32 = 0;
size_t fileSize = 0;
bool newFile = false;
bool locked = false;

dexFd = open(fileName, O_RDONLY); //打开文件
if (dexFd < 0) goto bail;
if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) { } //校验magic并读取校验和
if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) { } //获取修改时间和文件大小
if (odexOutputName == NULL) {
cachedName = dexOptGenerateCacheFileName(fileName, NULL);
} else {
cachedName = strdup(odexOutputName);
}
//打开优化后的dex,在其内部先检查修改时间与校验和,若存在优化后的
//文件且匹配原dex则直接返回,否重新生成优化后的文件
optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime,
adler32, isBootstrap, &newFile, /*createIfMissing=*/true);

if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) { } //最重要的地方

*ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
(*ppRawDexFile)->cacheFileName = cachedName;
(*ppRawDexFile)->pDvmDex = pDvmDex;
cachedName = NULL; // don't free it below
result = 0;

return result;
}

dvmDexFileOpenFromFd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
{
DvmDex* pDvmDex;
DexFile* pDexFile;
MemMapping memMap;
int parseFlags = kDexParseDefault;
int result = -1;

if (gDvm.verifyDexChecksum)
parseFlags |= kDexParseVerifyChecksum;
if (lseek(fd, 0, SEEK_SET) < 0) { }
if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) { } //将odex文件映射到内存

pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags); //对odex文件进行解析

pDvmDex = allocateAuxStructures(pDexFile);
sysCopyMap(&pDvmDex->memMap, &memMap);
pDvmDex->isMappedReadOnly = true;
*ppDvmDex = pDvmDex;
result = 0;
return result;
}

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
DexFile* dexFileParse(const u1* data, size_t length, int flags)
{
DexFile* pDexFile = NULL;
const DexHeader* pHeader;
const u1* magic;
int result = -1;

pDexFile = (DexFile*) malloc(sizeof(DexFile)); //创建DexFile
memset(pDexFile, 0, sizeof(DexFile));

if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {

pDexFile->pOptHeader = (const DexOptHeader*) data;

if (!dexParseOptData(data, length, pDexFile)) //解析odex文件,就是设置几个指针

data += pDexFile->pOptHeader->dexOffset;
length -= pDexFile->pOptHeader->dexOffset;
if (pDexFile->pOptHeader->dexLength > length) { }
length = pDexFile->pOptHeader->dexLength;
}

dexFileSetupBasicPointers(pDexFile, data); //再设置一些基本的指针
pHeader = pDexFile->pHeader;

result = 0;
return pDexFile;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*优化后的Dex添加的头*/
struct DexOptHeader {
u1 magic[8]; /* includes version number */

u4 dexOffset; /* file offset of DEX header */
u4 dexLength;
u4 depsOffset; /* offset of optimized DEX dependency table */
u4 depsLength;
u4 optOffset; /* file offset of optimized data tables */
u4 optLength;

u4 flags; /* some info flags */
u4 checksum; /* adler32 checksum covering deps/opt */

/* pad for 64-bit alignment if necessary */
};
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
struct DexFile {
/* directly-mapped "opt" header */
const DexOptHeader* pOptHeader;

/* pointers to directly-mapped structs and arrays in base DEX */
const DexHeader* pHeader;
const DexStringId* pStringIds;
const DexTypeId* pTypeIds;
const DexFieldId* pFieldIds;
const DexMethodId* pMethodIds;
const DexProtoId* pProtoIds;
const DexClassDef* pClassDefs;
const DexLink* pLinkData;

/*
* These are mapped out of the "auxillary" section, and may not be
* included in the file.
*/
const DexClassLookup* pClassLookup;
const void* pRegisterMapPool; // RegisterMapClassPool

/* points to start of DEX file data */
const u1* baseAddr;

/* track memory overhead for auxillary structures */
int overhead;
};

DexFile2Class

findClass

1
2
3
4
5
6
7
8
9
10
11
12
13
public Class findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;

if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
}
return null;
}

loadClassBinaryName

1
2
3
4
5
6
7
8
9
10
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
return defineClass(name, loader, mCookie, suppressed); //每个element的dexFile中有cookie即pDexOrJar
}

private static Class defineClass(String name, ClassLoader loader, int cookie, List<Throwable> suppressed) {
return defineClassNative(name, loader, cookie);
}

private static native Class defineClassNative(String name, ClassLoader loader, int cookie)
throws ClassNotFoundException, NoClassDefFoundError;

Dalvik_dalvik_system_DexFile_defineClassNative

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
 /*private static Class defineClassNative(String name, ClassLoader loader,int cookie))*/
static void Dalvik_dalvik_system_DexFile_defineClassNative(const u4* args,
JValue* pResult)
{
StringObject* nameObj = (StringObject*) args[0];
Object* loader = (Object*) args[1];
int cookie = args[2];
ClassObject* clazz = NULL;
DexOrJar* pDexOrJar = (DexOrJar*) cookie;
DvmDex* pDvmDex;
char* name;
char* descriptor;

name = dvmCreateCstrFromString(nameObj);
descriptor = dvmDotToDescriptor(name); //将类名转变为类描述符,如 java.String -> Ljava/String;

if (!validateCookie(cookie)) //判断cookie是否在hash表中,载入时会通过addToDexFileTable(pDexOrJar)将其加入

if (pDexOrJar->isDex) //对Dex和Zip分情况获取pDvmDex
pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
else
pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);

pDexOrJar->okayToFree = false;

clazz = dvmDefineClass(pDvmDex, descriptor, loader);//真正加载的地方

RETURN_PTR(clazz);
}

dvmDefineClass

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
ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor, Object* classLoader)
{
return findClassNoInit(descriptor, classLoader, pDvmDex);
}
static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
DvmDex* pDvmDex)
{
Thread* self = dvmThreadSelf();
ClassObject* clazz;
bool profilerNotified = false;

clazz = dvmLookupClass(descriptor, loader, true); //会尝试在loadedClasses这个hashTable中查找,找到说明类已加载过就直接返回
if (clazz == NULL) {
const DexClassDef* pClassDef;

dvmMethodTraceClassPrepBegin();
profilerNotified = true;

#if LOG_CLASS_LOADING
u8 startTime = dvmGetThreadCpuTimeNsec();
#endif

if (pDvmDex == NULL) {
assert(loader == NULL); /* shouldn't be here otherwise */
pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
} else {
pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor); //获取类定义的地址
}

if (pDvmDex == NULL || pClassDef == NULL) { }

clazz = loadClassFromDex(pDvmDex, pClassDef, loader); //加载类

dvmLockObject(self, (Object*) clazz);
clazz->initThreadId = self->threadId;

if (!dvmAddClassToHash(clazz)) { //将类添加到hashTable中
clazz = dvmLookupClass(descriptor, loader, true);
}
dvmReleaseTrackedAlloc((Object*) clazz, NULL);

if (!dvmLinkClass(clazz)) { }
dvmObjectNotifyAll(self, (Object*) clazz);
dvmUnlockObject(self, (Object*) clazz);

gDvm.numLoadedClasses++;
gDvm.numDeclaredMethods += clazz->virtualMethodCount + clazz->directMethodCount;
gDvm.numDeclaredInstFields += clazz->ifieldCount;
gDvm.numDeclaredStaticFields += clazz->sfieldCount;

}
return clazz;
}

loadClassFromDex

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static ClassObject* loadClassFromDex(DvmDex* pDvmDex,
const DexClassDef* pClassDef, Object* classLoader)
{
ClassObject* result;
DexClassDataHeader header;
const u1* pEncodedData;
const DexFile* pDexFile;

pDexFile = pDvmDex->pDexFile;

pEncodedData = dexGetClassData(pDexFile, pClassDef); //得到类数据的偏移

if (pEncodedData != NULL) {
dexReadClassDataHeader(&pEncodedData, &header); //对数据进行解压操作,得到域域方法的Size
} else {
// Provide an all-zeroes header for the rest of the loading.
memset(&header, 0, sizeof(header));
}

result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData,
classLoader);
return result;
}

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
147
148
149
static ClassObject* loadClassFromDex0(DvmDex* pDvmDex,
const DexClassDef* pClassDef, const DexClassDataHeader* pHeader,
const u1* pEncodedData, Object* classLoader)
{
ClassObject* newClass = NULL;
const DexFile* pDexFile;
const char* descriptor;
int i;

pDexFile = pDvmDex->pDexFile;
descriptor = dexGetClassDescriptor(pDexFile, pClassDef);

if (...){ } 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;
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;

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*));

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);
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);
}

/*载入类的方法*/
u4 classDefIdx = dexGetIndexForClassDef(pDexFile, pClassDef);
const void* classMapData;
u4 numMethods;

if (gDvm.preciseGc) {
classMapData =
dvmRegisterMapGetClassData(pDexFile, classDefIdx, &numMethods);

/* sanity check */
if (classMapData != NULL &&
pHeader->directMethodsSize + pHeader->virtualMethodsSize != numMethods)
{
ALOGE("ERROR: in %s, direct=%d virtual=%d, maps have %d",
newClass->descriptor, pHeader->directMethodsSize,
pHeader->virtualMethodsSize, numMethods);
assert(false);
classMapData = NULL; /* abandon */
}
} else {
classMapData = NULL;
}

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);

return newClass;
}

init

在此处才真正的实现初始化操作

dvmFindClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ClassObject* dvmFindClass(const char* descriptor, Object* loader)
{
ClassObject* clazz;

clazz = dvmFindClassNoInit(descriptor, loader); //
if (clazz != NULL && clazz->status < CLASS_INITIALIZED) {
/* initialize class */
if (!dvmInitClass(clazz)) {
/* init failed; leave it in the list, marked as bad */
assert(dvmCheckException(dvmThreadSelf()));
assert(clazz->status == CLASS_ERROR);
return NULL;
}
}

return clazz;
}

dvmInitClass

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
bool dvmInitClass(ClassObject* clazz)
{
u8 startWhen = 0;

#if LOG_CLASS_LOADING
bool initializedByUs = false;
#endif

Thread* self = dvmThreadSelf();
const Method* method;

dvmLockObject(self, (Object*) clazz);

noverify:

if (!IS_CLASS_FLAG_SET(clazz, CLASS_ISOPTIMIZED) && !gDvm.optimizing) {
bool essentialOnly = (gDvm.dexOptMode != OPTIMIZE_MODE_FULL);
dvmOptimizeClass(clazz, essentialOnly); //优化类中的方法,此处会涉及到指令的修改!
SET_CLASS_FLAG(clazz, CLASS_ISOPTIMIZED);
}

dvmFlushBreakpoints(clazz);

if (gDvm.allocProf.enabled) {
startWhen = dvmGetRelativeTimeNsec();
}

/* order matters here, esp. interaction with dvmIsClassInitializing */
clazz->initThreadId = self->threadId;
android_atomic_release_store(CLASS_INITIALIZING,
(int32_t*)(void*)&clazz->status);
dvmUnlockObject(self, (Object*) clazz);

/* 初始化父类 */
if (clazz->super != NULL && clazz->super->status != CLASS_INITIALIZED) {
assert(!dvmIsInterfaceClass(clazz));
if (!dvmInitClass(clazz->super)) {
assert(dvmCheckException(self));
clazz->status = CLASS_ERROR;
/* wake up anybody who started waiting while we were unlocked */
dvmLockObject(self, (Object*) clazz);
goto bail_notify;
}
}

/*初始化静态域 final static */
initSFields(clazz);

/*初始化代码*/
method = dvmFindDirectMethodByDescriptor(clazz, "<clinit>", "()V");
if (method == NULL) {} else {
dvmCallMethod(self, method, NULL, &unused);
}

if (dvmCheckException(self)) { } else {
/* success! */
dvmLockObject(self, (Object*) clazz);
clazz->status = CLASS_INITIALIZED;
LOGVV("Initialized class: %s", clazz->descriptor);
if (gDvm.allocProf.enabled && startWhen != 0) {
u8 initDuration = dvmGetRelativeTimeNsec() - startWhen;
gDvm.allocProf.classInitTime += initDuration;
self->allocProf.classInitTime += initDuration;
gDvm.allocProf.classInitCount++;
self->allocProf.classInitCount++;
}
}

bail_notify:

dvmObjectNotifyAll(self, (Object*) clazz);

bail_unlock:

#if LOG_CLASS_LOADING
if (initializedByUs) {
// We finished initializing.
logClassLoad('-', clazz);
}
#endif

dvmUnlockObject(self, (Object*) clazz);

return (clazz->status != CLASS_ERROR);
}

参考

https://blog.csdn.net/itermeng/article/details/75669628
https://blog.csdn.net/javazejian/article/details/73413292