今天主要来分析Dex动态加载C++语言的部分(Android4.0)
内存加载:
native代码:private static int openDexFile(byte[] fileContents) throws IOException
所对应的实现如下:
static void Dalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4* args , JValue* pResult)
{
ArrayObject* fileContentsObj = (ArrayObject*) args[0];
u4 length;
u1* pBytes;
RawDexFile* pRawDexFile;
DexOrJar* pDexOrJar = NULL;
if (fileContentsObj == NULL) {
dvmThrowNullPointerException("fileContents == null");
RETURN_VOID();
}
length = fileContentsObj->length;
pBytes = (u1*) malloc(length);
if (pBytes == NULL) {
dvmThrowRuntimeException("unable to allocate DEX memory");
RETURN_VOID();
}
memcpy(pBytes, fileContentsObj->contents, length);
if (dvmRawDexFileOpenArray(pBytes, length, &pRawDexFile) != 0) {
LOGV("Unable to open in-memory DEX file");
free(pBytes);
dvmThrowRuntimeException("unable to open in-memory DEX file");
RETURN_VOID();
}
LOGV("Opening in-memory DEX");
pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
pDexOrJar->isDex = true;
pDexOrJar->pRawDexFile = pRawDexFile;
pDexOrJar->pDexMemory = pBytes;
pDexOrJar->fileName = strdup("<memory>"); // Needs to be free()able.
addToDexFileTable(pDexOrJar);
RETURN_PTR(pDexOrJar);
}
首先来解释fileContentsObj这个对象的内容,从Java层传过来的byte[]类型的一个对象,当到达c++语言层的时候成了两个参数,args和pResult,然后把args这个数组中
的第一个元素直接转化成了ArrayObject对象,接下来显而易见,把这个元素的内容赋给了pBytes对象,那么这个总体的过程可以理解为一个Java层的byte[]类型的对象
转化为c++语言层的一个byte[]类型的对象;
接下来将是对dvmRawDexFileOpenArray(pBytes, length, &pRawDexFile)这个函数的分析,在分析之前,可以一起来看一下这个函数的三个参数,第一个参数和第二个
参数已经不用说了,就是一个byte[]类型的数组,以及这个数组中内容的长度,其实这个内容就是dex的字节流信息,第三个参数是一个RawDexFile的对象,这个对象的数据
结构是这样的:
struct RawDexFile {
char* cacheFileName;
DvmDex* pDvmDex;
};
这里又看到一个新的数据对象DvmDex,再来看一下这个数据结构:
struct DvmDex {
DexFile* pDexFile;
const DexHeader* pHeader;
struct StringObject** pResStrings;
struct ClassObject** pResClasses;
struct Method** pResMethods;
struct Field** pResFields;
struct AtomicCache* pInterfaceCache;
bool isMappedReadOnly;
MemMapping memMap;
pthread_mutex_t modLock;
};
这里又看到一个新的数据对象DexFile,再来看一下这个数据结构:
struct DexFile {
const DexOptHeader* pOptHeader;
const DexHeader* pHeader;
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; // RegisterMapClassPool
const u1* baseAddr;
int overhead;
};
这个数据结构如果了解dex文件格式的话,就对这里的一些对象比较清楚了,其实这个DvmDex数据结构中就保存了Dex文件的各个部分的一些相关内容,具体可以看一下Dex文件
的格式,这里不做多详细的介绍
现在回到那个方法,我们继续往下玩dvmRawDexFileOpenArray这个函数,
int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppRawDexFile)
{
DvmDex* pDvmDex = NULL;
if (!dvmPrepareDexInMemory(pBytes, length, &pDvmDex)) {
LOGD("Unable to open raw DEX from array");
return -1;
}
assert(pDvmDex != NULL);
*ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
(*ppRawDexFile)->pDvmDex = pDvmDex;
return 0;
}
我们可以看到进入这个函数的第一步就是建立了一个DvmDex类型的对象,并初始化为空,经过前面的分析,我们都应该有这个联系,这个pDvmDex对象不就是包含于RawDexFile
这个数据结构中的吗,再看最后第三行,(*ppRawDexFile)->pDvmDex = pDvmDex;,不用多解释,大家明白了这个赋值操作的含义,现在就来看看这个函数的中间部分到底
做了什么呢,还不是dvmPrepareDexInMemory(pBytes, length, &pDvmDex)这个东西惹的祸吗,我们继续进入其中作战,
bool dvmPrepareDexInMemory(u1* addr, size_t len, DvmDex** ppDvmDex)
{
DexClassLookup* pClassLookup = NULL;
if (!rewriteDex(addr, len, false, false, &pClassLookup, ppDvmDex)) {
return false;
}
(*ppDvmDex)->pDexFile->pClassLookup = pClassLookup;
return true;
}
先来看下DexClassLookup这个数据结构长啥模样吧,
struct DexClassLookup {
int size;
int numEntries;
struct {
u4 classDescriptorHash;
int classDescriptorOffset;
int classDefOffset;
} table[1];
};
其实很简单,都是一些最基本的数据类型,也不知道这些代码代码什么意思,黑灯瞎火的,继续向下走吧,
现在来看下rewriteDex这个函数干了些什么,
static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
DexClassLookup** ppClassLookup, DvmDex** ppDvmDex)
{
DexClassLookup* pClassLookup = NULL;
u8 prepWhen, loadWhen, verifyOptWhen;
DvmDex* pDvmDex = NULL;
bool result = false;
const char* msgStr = "???";
if (dexSwapAndVerify(addr, len) != 0)
goto bail;
if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
LOGE("Unable to create DexFile");
goto bail;
}
pClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
if (pClassLookup == NULL)
goto bail;
pDvmDex->pDexFile->pClassLookup = pClassLookup;
//doVerify和doOpt的值都为false,所以直接跳转到bail,于是这个函数就又执行完了,下面再回溯到前面为执行完的代码
if (!doVerify && !doOpt) {
result = true;
goto bail;
}
prepWhen = dvmGetRelativeTimeUsec();
if (!loadAllClasses(pDvmDex))
goto bail;
loadWhen = dvmGetRelativeTimeUsec();
if (!dvmCreateInlineSubsTable())
goto bail;
verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt);
verifyOptWhen = dvmGetRelativeTimeUsec();
if (doVerify && doOpt)
msgStr = "verify+opt";
else if (doVerify)
msgStr = "verify";
else if (doOpt)
msgStr = "opt";
LOGD("DexOpt: load %dms, %s %dms",
(int) (loadWhen - prepWhen) / 1000,
msgStr,
(int) (verifyOptWhen - loadWhen) / 1000);
result = true;
bail:
if (pDvmDex != NULL) {
pDvmDex->pDexFile->pClassLookup = NULL;
}
if (ppDvmDex == NULL || !result) {
dvmDexFileFree(pDvmDex);
} else {
*ppDvmDex = pDvmDex;
}
if (ppClassLookup == NULL || !result) {
free(pClassLookup);
} else {
*ppClassLookup = pClassLookup;
}
return result;
}
一看可能先吓了一跳,怎么这个函数一下子多了那么多代码,有点不敢看了,其实我想说这是好事啊,这说明你离成功已经不远啦,战士们,Come On吧,
dexSwapAndVerify这个函数就不详细讲了,有兴趣的可以自己详细看,主要是用来交换Dex文件的的字节码的顺序的,这个和你使用的系统处理器是大端的还是小端的有密切
的关系;
接下来看dvmDexFileOpenPartial(addr, len, &pDvmDex)这个函数:
int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
{
DvmDex* pDvmDex;
DexFile* pDexFile;
int parseFlags = kDexParseDefault;
int result = -1;
pDexFile = dexFileParse((u1*)addr, len, parseFlags);
if (pDexFile == NULL) {
LOGE("DEX parse failed");
goto bail;
}
pDvmDex = allocateAuxStructures(pDexFile);
if (pDvmDex == NULL) {
dexFileFree(pDexFile);
goto bail;
}
pDvmDex->isMappedReadOnly = false;
*ppDvmDex = pDvmDex;
result = 0;
bail:
return result;
}
这里可以看到新建了一个DexFile类型的对象,这个主要是用来描述Dex的整个文件的信息,上面已经介绍过了,
接下来看其中的pDexFile = dexFileParse((u1*)addr, len, parseFlags)这个函数,
(这里先说明吓,parseFlags这个参数的值是kDexParseDefault赋予的,kDexParseDefault是一个枚举变量,它的值为0,所以parseFlags的值为0;)
DexFile* dexFileParse(const u1* data, size_t length, int flags)
{
DexFile* pDexFile = NULL;
const DexHeader* pHeader;
const u1* magic;
int result = -1;
//判断dex文件得长度是不是大于dex头部得长度,如果比头部的长度都小,那就什么都可以免谈了,好吗!!!
if (length < sizeof(DexHeader)) {
LOGE("too short to be a valid .dex");
goto bail; /* bad file format */
}
//如果dex文件长度是合法的,那么可以给这个还未初始化的pDexFile对象进行全0的初始化了
pDexFile = (DexFile*) malloc(sizeof(DexFile));
if (pDexFile == NULL)
goto bail; /* alloc failure */
memset(pDexFile, 0, sizeof(DexFile));
//这里主要是用来判断是否这个dex文件是一个优化过的dex文件,如果是优化过的dex文件,那么就执行里面的操作,如果是未优化过的,直接可以跳过这一整个
//if操作了,为了简单起见,暂时就先跳过这个处理dex opt头部的操作吧
if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
magic = data;
if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
LOGE("bad opt version (0x%02x %02x %02x %02x)",
magic[4], magic[5], magic[6], magic[7]);
goto bail;
}
pDexFile->pOptHeader = (const DexOptHeader*) data;
LOGV("Good opt header, DEX offset is %d, flags=0x%02x",
pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);
/* parse the optimized dex file tables */
if (!dexParseOptData(data, length, pDexFile))
goto bail;
/* ignore the opt header and appended data from here on out */
data += pDexFile->pOptHeader->dexOffset;
length -= pDexFile->pOptHeader->dexOffset;
if (pDexFile->pOptHeader->dexLength > length) {
LOGE("File truncated? stored len=%d, rem len=%d",
pDexFile->pOptHeader->dexLength, (int) length);
goto bail;
}
length = pDexFile->pOptHeader->dexLength;
}
//好了,刚才我们已经跳过了这个处理dex opt头部的操作,下面我们就继续战斗,离结束不远啦,坚持住阿!!!
//下面将来看dexFileSetupBasicPointers(pDexFile,data)这个函数,相信有意识的都应该此刻开始兴奋了,因为这就是关键点了!详细分析请看下面
dexFileSetupBasicPointers(pDexFile, data);
pHeader = pDexFile->pHeader;
//再一次判断头部的magic的8个字节是否是合法的
if (!dexHasValidMagic(pHeader)) {
goto bail;
}
//这里flags为0,不用再纠结了,跳过吧
if (flags & kDexParseVerifyChecksum) {
u4 adler = dexComputeChecksum(pHeader);
if (adler != pHeader->checksum) {
LOGE("ERROR: bad checksum (%08x vs %08x)",
adler, pHeader->checksum);
if (!(flags & kDexParseContinueOnError))
goto bail;
} else {
LOGV("+++ adler32 checksum (%08x) verified", adler);
}
const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
if (pOptHeader != NULL) {
adler = dexComputeOptChecksum(pOptHeader);
if (adler != pOptHeader->checksum) {
LOGE("ERROR: bad opt checksum (%08x vs %08x)",
adler, pOptHeader->checksum);
if (!(flags & kDexParseContinueOnError))
goto bail;
} else {
LOGV("+++ adler32 opt checksum (%08x) verified", adler);
}
}
}
//这里kVerifySignature为0,不用纠结了,跳过吧
if (kVerifySignature) {
unsigned char sha1Digest[kSHA1DigestLen];
const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +
kSHA1DigestLen;
dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);
if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {
char tmpBuf1[kSHA1DigestOutputLen];
char tmpBuf2[kSHA1DigestOutputLen];
LOGE("ERROR: bad SHA1 digest (%s vs %s)",
dexSHA1DigestToStr(sha1Digest, tmpBuf1),
dexSHA1DigestToStr(pHeader->signature, tmpBuf2));
if (!(flags & kDexParseContinueOnError))
goto bail;
} else {
LOGV("+++ sha1 digest verified");
}
}
//继续判断dex文件的大小是否合法
if (pHeader->fileSize != length) {
LOGE("ERROR: stored file size (%d) != expected (%d)",
(int) pHeader->fileSize, (int) length);
if (!(flags & kDexParseContinueOnError))
goto bail;
}
//判断dex文件中累定义的个数
if (pHeader->classDefsSize == 0) {
LOGE("ERROR: DEX file has no classes in it, failing");
goto bail;
}
/*
* Success!
*/
result = 0;
bail:
if (result != 0 && pDexFile != NULL) {
dexFileFree(pDexFile);
pDexFile = NULL;
}
return pDexFile;
}
***********************************************************************************************************************************
承接上部分对dexFileSetupBasicPointers(pDexFile, data)的分析:
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);
}
可以看到如此简单的几条语句,难道就是核心部分嘛,是的,可以很开心地告诉你,这是非常核心的部分:
Dex总共可以分八个部分,Header,StringID,TypeID,FieldID,MethodID,ProtoID,ClassDefID,CODE,这八个部分本来就是连在一块的大肉饼,
千里迢迢地从java层一直跑到C++层,但是从这一刻起,就在调用完dexFileSetupBasicPointers函数地这一刻起,Dex的整个结构一下子被“大卸八块”了,完全充斥着
pDexFile这个结构体了,也可以很光荣地告诉你要是再想访问Dex中地任何内容,你只需访问pDexFile这个结构体对象即可!!!,好了,我们继续回到上面的下一条语句开始
分析,不要让兴奋冲昏了头脑,毕竟这还没有大获全胜阿,加油,Comn on!!!
***********************************************************************************************************************************
分析完了这个dexFileParse((u1*)addr, len, parseFlags)函数过后,我们可以返回上一层的dvmDexFileOpenPartial这个函数的下一条语句了,
从这里开始:
if (pDexFile == NULL) {
LOGE("DEX parse failed");
goto bail;
}
pDvmDex = allocateAuxStructures(pDexFile);
if (pDvmDex == NULL) {
dexFileFree(pDexFile);
goto bail;
}
pDvmDex->isMappedReadOnly = false;
*ppDvmDex = pDvmDex;
result = 0;
bail:
return result;
}
好了,这里我们又要分析一个关键的函数了,刚才把pDexFile喂饱了,那么干脆好人做到底啦,是不是应该要把pDvmDex这个对象也要喂饱呢??
我们这就来分析allocateAuxStructures(pDexFile)这个函数吧,
static DvmDex* allocateAuxStructures(DexFile* pDexFile)
{
//前三行初始化对象,不用多说了,就等着待会儿被赋值吧
DvmDex* pDvmDex;
const DexHeader* pHeader;
u4 stringCount, classCount, methodCount, fieldCount;
//给pDvmDex这个对象分配内存,赋值为全1
pDvmDex = (DvmDex*) calloc(1, sizeof(DvmDex));
if (pDvmDex == NULL)
return NULL;
//这两句语开始给pDvmDex这个结构体中的变量赋值
pDvmDex->pDexFile = pDexFile;
pDvmDex->pHeader = pDexFile->pHeader;
//这五条语句主要是给这个函数的5个局部变量赋值
pHeader = pDvmDex->pHeader;
stringCount = pHeader->stringIdsSize;
classCount = pHeader->typeIdsSize;
methodCount = pHeader->methodIdsSize;
fieldCount = pHeader->fieldIdsSize;
//下面这四条语句给pDvmDex这个对象中的关键的四个字段进行分配空间
pDvmDex->pResStrings = (struct StringObject**)
calloc(stringCount, sizeof(struct StringObject*));
pDvmDex->pResClasses = (struct ClassObject**)
calloc(classCount, sizeof(struct ClassObject*));
pDvmDex->pResMethods = (struct Method**)
calloc(methodCount, sizeof(struct Method*));
pDvmDex->pResFields = (struct Field**)
calloc(fieldCount, sizeof(struct Field*));
LOGV("+++ DEX %p: allocateAux %d+%d+%d+%d * 4 = %d bytes",
pDvmDex, stringCount, classCount, methodCount, fieldCount,
(stringCount + classCount + methodCount + fieldCount) * 4);
//这条语句其实也是一样的,也是给pDvmDex中的pInterfaceCache对象分配空间,以及赋值而已
pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);
if (pDvmDex->pResStrings == NULL ||
pDvmDex->pResClasses == NULL ||
pDvmDex->pResMethods == NULL ||
pDvmDex->pResFields == NULL ||
pDvmDex->pInterfaceCache == NULL)
{
LOGE("Alloc failure in allocateAuxStructures");
free(pDvmDex->pResStrings);
free(pDvmDex->pResClasses);
free(pDvmDex->pResMethods);
free(pDvmDex->pResFields);
free(pDvmDex);
return NULL;
}
return pDvmDex;
}
看到这里,你觉得pDvmDex这个对象被喂饱了吗,有没有喂饱就决定了你到底对这个源代码理解了多少,我们继续向下:
刚才dexFileParse((u1*)addr, len, parseFlags) 剩下的部分是这些:
if (pDvmDex == NULL) {
dexFileFree(pDexFile);
goto bail;
}
pDvmDex->isMappedReadOnly = false;
*ppDvmDex = pDvmDex;
result = 0;
bail:
return result;
}
显然可以明白是什么意思,不用多说了,那么我们要返回上上个函数没执行完多部分了,就是rewriteDex这个函数,
我们还是从这个函数的上次没执行玩的第一条语句开始吧:
//回顾下,pClassLoopup是DexClassLookup类型的对象,这个对象的主要是用来后续方便查找和加载类的,见下面详细分析dexCreateClassLookup这个函数:
pClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
if (pClassLookup == NULL)
goto bail;
pDvmDex->pDexFile->pClassLookup = pClassLookup;
//这里的doVeriry和doOpt的值为false,所以可以直接跳进入bail了
if (!doVerify && !doOpt) {
result = true;
goto bail;
}
prepWhen = dvmGetRelativeTimeUsec();
if (!loadAllClasses(pDvmDex))
goto bail;
loadWhen = dvmGetRelativeTimeUsec();
if (!dvmCreateInlineSubsTable())
goto bail;
verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt);
verifyOptWhen = dvmGetRelativeTimeUsec();
if (doVerify && doOpt)
msgStr = "verify+opt";
else if (doVerify)
msgStr = "verify";
else if (doOpt)
msgStr = "opt";
LOGD("DexOpt: load %dms, %s %dms",
(int) (loadWhen - prepWhen) / 1000,
msgStr,
(int) (verifyOptWhen - loadWhen) / 1000);
result = true;
bail:
if (pDvmDex != NULL) {
pDvmDex->pDexFile->pClassLookup = NULL;
}
if (ppDvmDex == NULL || !result) {
dvmDexFileFree(pDvmDex);
} else {
*ppDvmDex = pDvmDex;
}
if (ppClassLookup == NULL || !result) {
free(pClassLookup);
} else {
*ppClassLookup = pClassLookup;
}
return result;
}
**************************************************************
DexClassLookup* dexCreateClassLookup(DexFile* pDexFile)
{
DexClassLookup* pLookup;
int allocSize;
int i, numEntries;
int numProbes, totalProbes, maxProbes;
numProbes = totalProbes = maxProbes = 0;
assert(pDexFile != NULL);
//dexRoundUpPower2的函数的作用是把一个整数向上转化为一个2的幂,比如:1就是1,2就是2,3就是4,5就是8等等......
numEntries = dexRoundUpPower2(pDexFile->pHeader->classDefsSize * 2);
//整个LoopUp表的大小,这里可以看到,有多少类,就有多大的空间,当然numEnties已经在现有的类的个数上翻了一番
allocSize = offsetof(DexClassLookup, table)
+ numEntries * sizeof(pLookup->table[0]);
//为pLookup这个对象分配存储空间
pLookup = (DexClassLookup*) calloc(1, allocSize);
if (pLookup == NULL)
return NULL;
//给pLookup结构体内的字段size,numEntries赋值
pLookup->size = allocSize;
pLookup->numEntries = numEntries;
//下面开始给pLookup这个对象中的table字段赋值,循环的对把各个类的信息加入到pLookup这张表中
for (i = 0; i < (int)pDexFile->pHeader->classDefsSize; i++) {
const DexClassDef* pClassDef;
const char* pString;
//通过索引i来获得一个DexClassDef类型的对象
pClassDef = dexGetClassDef(pDexFile, i);
//通过类中的类型索引来找到类类型,返回一个字符串
pString = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
//classLookupAdd函数就是把第一个类的加入到pLookup这张表中
//下面先来分析下这个函数,详细分析见下面
classLookupAdd(pDexFile, pLookup,
(u1*)pString - pDexFile->baseAddr,
(u1*)pClassDef - pDexFile->baseAddr, &numProbes);
if (numProbes > maxProbes)
maxProbes = numProbes;
totalProbes += numProbes;
}
LOGV("Class lookup: classes=%d slots=%d (%d%% occ) alloc=%d"
" total=%d max=%d",
pDexFile->pHeader->classDefsSize, numEntries,
(100 * pDexFile->pHeader->classDefsSize) / numEntries,
allocSize, totalProbes, maxProbes);
return pLookup;
}
//好了,到这里为止,dexCreateClassLookup这个函数建立pLookup这个表的过程算是结束了,我们开始回溯到上面的未完成的代码中
**************************************************************
//下面来解释classLoopupAdd这个函数
static void classLookupAdd(DexFile* pDexFile, DexClassLookup* pLookup,
int stringOff, int classDefOff, int* pNumProbes)
{
//得到类描述的一个字符串,其实就是类的类型
const char* classDescriptor =
(const char*) (pDexFile->baseAddr + stringOff);
//得到类的一个类定义对象
const DexClassDef* pClassDef =
(const DexClassDef*) (pDexFile->baseAddr + classDefOff);
//将类描述符通过这个hash函数进行hash,返回一个无符号整形的hash值
u4 hash = classDescriptorHash(classDescriptor);
int mask = pLookup->numEntries-1;
int idx = hash & mask;
int probes = 0;
//这里就开始查找pLookup这张表中空余的位置,来放置上面得到的类的信息
while (pLookup->table[idx].classDescriptorOffset != 0) {
idx = (idx + 1) & mask;
probes++;
}
pLookup->table[idx].classDescriptorHash = hash;
pLookup->table[idx].classDescriptorOffset = stringOff;
pLookup->table[idx].classDefOffset = classDefOff;
*pNumProbes = probes;
}
//好了,到这里为止,已经把第一个类的信息放在了pLookup这张表中了,我们继续返回上面的代码
*************************************************************** 来来回回地终于把内存加载字节码的功能分析完了,中间可能漏了很多东西没有细讲,还有一些可能讲的不对,还望大牛多多批评指教,第一次分析源码,写的有点乱,
请看不懂的朋友随时联系我,我一定会改正的;
希望通过这个过程能够帮助有需要的人,也是对我自己的一次磨练!谢谢
请各位大牛批评指教!!!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)