5.2 odex文件

odex是OptimizedDEX的缩写,是优化过的dex文件

odex两种存在方式:

1. 从apk程序中提取,和apk文件放在一起,后缀 odex,此类文件多是AndroidRom系统文件

2. 在cache/dalvik-cache缓存文件,后缀 dex

a)      Eg:system@app@calcuator.apk@classes.dex 安装在/system/app目录下calcuator.apk程序的odex文件

odex作用:

因为Dalvik每次加载从apk中读取classes.dex文件会消耗cpu时间,odex则已经包含了需要加载的库文件列表,Dalvik虚拟机加载时根据需要加载的库对照dex文件即可。

部分Android系统的ROM将系统odex文件与app放在同一目录,系统在启动加载这些程序会更省时间。

 

一、生成odex文件:

使用Android系统源码工具生成dex

build/tools/dexpreopt/dexopt-wrapper下的dexopt-wrapper拷入真机adb push命令

给予777权限,将需要生成dex文件拷入到手机cd跳转该目录执行如下命令

adb pull将文件拷出得到odex文件,文件位置默认为此时cmd的路径位置

 

分析odex文件:

文件结构体

Dalvik虚拟机将dex文件映射到内存后

500struct DexFile {
501    /* directly-mapped "opt" header */
502    const DexOptHeader* pOptHeader;          // odex文件头
503
504    /* pointers to directly-mapped structs and arrays in base DEX */
505    const DexHeader*    pHeader;                        
506    const DexStringIdpStringIds;
507    const DexTypeId*    pTypeIds;
508    const DexFieldId*   pFieldIds;
509    const DexMethodIdpMethodIds;
510    const DexProtoId*   pProtoIds;
511    const DexClassDefpClassDefs;
512    const DexLink*      pLinkData;
513
514    /*
515     * 辅助数据段,记录文件被优化后添加的一些信息
517     */
518    const DexClassLookup* pClassLookup;
519    const void*         pRegisterMapPool;       // RegisterMapClassPool
520
521    /* points to start of DEX file data */
522    const u1*           baseAddr;
523
524    /* track memory overhead for auxillary structures */
525    int                 overhead;
526
527    /* additional app-specific data structures associated with the DEX */
528    //void*               auxData;
529};

DexFile结构中存入其他结构的指针,描述的是加载到内存的数据结构,还有些数据是不会加载到内存的

odex文件结构

struct ODEXFile{

       DexOptHeader  header;// odex文件头

       DEXFile dexfile;// dex文件

       Dependence  deps;//依赖库列表

       ChunkDexClassLoopup       lookup;// 类查询结构

       ChunkRegisterMapPool  mappool;// 映射池

       ChunkEnd     end;// 结束标志

}

 

二、odex文件解析
       DexOptHeader在DexFile.h文件中
466
/*

467 * Header added by DEX optimization pass.  Values are always written in
468 * local byte and structure padding.  The first field (magic + version)
469 * is guaranteed to be present and directly readable for all expected
470 * compiler configurations; the rest is version-dependent.
471 *
472 * Try to keep this simple and fixed-size.
473 */
474struct DexOptHeader {

475    u1  magic[8];           /* odex版本标示 目前固定“64 65 79 0A 30 33 36 00” dey 036 */

476
477    u4  dexOffset;          /* dex文件头偏移 目前固定为“28 00 00 00”*/
478    u4  dexLength;          /* dex文件总长度 */
479    u4  depsOffset;         /* odex依赖库列表偏移 */
480    u4  depsLength;         /* 依赖库列表总长度 */
481    u4  optOffset;          /* 辅助数据偏移 */
482    u4  optLength;         /* 辅助数据总长度 */
483
484    u4  flags;              /* 标志,标识了Dalvik虚拟机加载odex时的优化与验证选项 */
485    u4  checksum;           /* 依赖库与辅助数据的校验和*/
486
487    /* pad for 64-bit alignment if necessary */
488};
        DexOptheader结构以下为DEXFile。
        DEXFile下为Dependences结构,Dependences结构不会加载到内存,并且Android源码没有明确定义。
整理出来的结构
struct DexOptHeader{
        u4 modWhen;            // 时间戳
        u4 crc;        // 校验
        u4 DALVIK_VM_BUILD;            // Dalvik虚拟机版本号
        u4 numDeps;            // 依赖库的个数
        struct{ 
               u4 len;        // name字符串长度
               u1 name[len];  // 依赖库的名称,依赖库的完整路径
               kSHA1DigestLen signature;      // SHA-1 哈希值
        }table[numDeps];       // numDeps决定了table连续的个数
};
        Dependences结构的具体操作函数位置 dalvikvmanalysisDexPrepare.cpp 中的writeDependencies()
1358
/*
1359 * Write the dependency info to "fd" at the current file position.
1360 */
1361static int writeDependencies(int fd, u4 modWhen, u4 crc)
1362{
1363    u1* buf = NULL;
1364    int result = -1;
1365    ssize_t bufLen;
1366    ClassPathEntry* cpe;
1367    int numDeps;
1368
1369    /*
1370     * Count up the number of completed entries in the bootclasspath.
1371     */
1372    numDeps = 0;
1373    bufLen = 0;
1374    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
1375        const char* cacheFileName =
1376            dvmPathToAbsolutePortion(getCacheFileName(cpe));
1377        assert(cacheFileName != NULL); /* guaranteed by Class.c */
1378
1379        ALOGV("+++ DexOpt: found dep '%s'", cacheFileName);
1380
1381        numDeps++;
1382        bufLen += strlen(cacheFileName) +1;
1383    }
1384
1385    bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
1386
1387    buf = (u1*)malloc(bufLen);
1388
1389    set4LE(buf+0, modWhen);              // 写入时间戳                  注意:modWhenhe和crc通过
1390    set4LE(buf+4, crc);                  // 写入crc校验         dexZipGetEntryInfo()获取的     
1391    set4LE(buf+8, DALVIK_VM_BUILD);      // 写入Dalvik虚拟机版本号
1392    set4LE(buf+12, numDeps);             // 写入依赖库的个数
1393
1394    // TODO: do we want to add dvmGetInlineOpsTableLength() here?  Won't
1395    // help us if somebody replaces an existing entry, but it'd catch
1396    // additions/removals.
1397
1398    u1* ptr = buf + 4*4;                 // 跳过前四个字段
1399    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {           // 循环写入依赖库
1400        const char* cacheFileName =
1401            dvmPathToAbsolutePortion(getCacheFileName(cpe));
1402        assert(cacheFileName != NULL); /* guaranteed by Class.c */
1403
1404        const u1* signature = getSignature(cpe);      // 计算SHA-1 哈希值
1405        int len = strlen(cacheFileName) +1;
1406
1407        if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
1408            ALOGE("DexOpt: overran buffer");
1409            dvmAbort();
1410        }
1411
1412        set4LE(ptr, len);
1413        ptr += 4;
1414        memcpy(ptr, cacheFileName, len);         // 写入依赖库的名字
1415        ptr += len;
1416        memcpy(ptr, signature, kSHA1DigestLen);          // 写入SHA-1哈希值
1417        ptr += kSHA1DigestLen;
1418    }
1419
1420    assert(ptr == buf + bufLen);
1421
1422    result = sysWriteFully(fd, buf, bufLen, "DexOpt dep info");
1423
1424    free(buf);
1425    return result;
1426}
    dexZipGetEntryInfo()函数位于 /dalvik/libdex/ZipArchive.cpp       根据结构体分析二进制即可

       Dalvik版本号:Android2.2.3            19

                             Android2.3~2.3.7 23   

                             Android4.0~4.1          27

    Dependences结构下有3个Chunk块。由/dalvik/vm/analysis/DexPrepare.cpp中的writeOptData()写入
1474
1475 * Write opt data.
1476 *
1477 * We have different pieces, some of which may be optional.  To make the
1478 * most effective use of space, we use a "chunk" format, with a 4-byte
1479 * type and a 4-byte length.  We guarantee 64-bit alignment for the data,
1480 * so it can be used directly when the file is mapped for reading.
1481 */
1482static bool writeOptData(int fd, const DexClassLookup* pClassLookup,
1483    const RegisterMapBuilder* pRegMapBuilder)
1484{
1485    /* pre-computed class lookup hash table */
1486    if (!writeChunk(fd, (u4) kDexChunkClassLookup,
1487            pClassLookup, pClassLookup->size))
1488    {
1489        return false;
1490    }
1491
1492    /* register maps (optional) */
1493    if (pRegMapBuilder != NULL) {
1494        if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
1495                pRegMapBuilder->data, pRegMapBuilder->size))
1496        {
1497            return false;
1498        }
1499    }
1500
1501    /* write the end marker */
1502    if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
1503        return false;
1504    }
1505
1506    return true;
1507}
        数据是通过writeChunk()写入的,writeChunk()源码
1429
/*
1430 * Write a block of data in "chunk" format.
1431 *
1432 * header结构体占8字节,type字段为1一个kDexChunk开头的常量
1433 * 
1434 */
1435static bool writeChunk(int fd, u4 type, const void* data, size_t size)
1436{
1437    union {             /* save a syscall by grouping these together */
1438        char raw[8];
1439        struct {
1440            u4 type;
1441            u4 size;
1442        } ts;
1443    } header;
1444
1445    assert(sizeof(header) == 8);
1446
1447    ALOGV("Writing chunk, type=%.4s size=%d", (char*) &type, size);
1448
1449    header.ts.type = type;
1450    header.ts.size = (u4) size;
1451    if (sysWriteFully(fd, &header, sizeof(header),
1452            "DexOpt opt chunk header write") != 0)
1453    {
1454        return false;
1455    }
1456
1457    if (size > 0) {
1458        if (sysWriteFully(fd, data, size, "DexOpt opt chunk write") != 0)
1459            return false;
1460    }
1461
1462    /* if necessary, pad to 64-bit alignment */
1463    if ((size & 7) != 0) {
1464        int padSize = 8 - (size & 7);
1465        ALOGV("size was %d, inserting %d pad bytes", size, padSize);
1466        lseek(fd, padSize, SEEK_CUR);
1467    }
1468
1469    assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
1470
1471    return true;
1472}
 
    writeChunk()方法中传入的type字段
188/* auxillary data section chunk codes */
189enum {
190    kDexChunkClassLookup            = 0x434c4b50,   /* CLKP */
191    kDexChunkRegisterMaps           = 0x524d4150,   /* RMAP */
192
193    kDexChunkEnd                    = 0x41454e44,   /* AEND */
194};
    
    writeOptData ()方法中传入DexClassLookup结构指针,Dalvik虚拟机通过DexClassLookup结构检索dex文件中的类
447/*
448 * Lookup table for classes.  It provides a mapping from class name to
449 * class definition.  Used by dexFindClass().
450 *
451 * We calculate this at DEX optimization time and embed it in the file so we
452 * don't need the same hash table in every VM.  This is slightly slower than
453 * a hash table with direct pointers to the items, but because it's shared
454 * there's less of a penalty for using a fairly sparse table.
455 */
456struct DexClassLookup {
457    int     size;                       // 本结构的字节数
458    int     numEntries;                 // 接下来table结构的项数,通常值为2
459    struct {
460        u4      classDescriptorHash;    // 类的哈希值
461        int     classDescriptorOffset// 类的描述
462        int     classDefOffset;         // 指向DexClassDef结构的指针
463    } table[1];// 用来描述类的信息
464};
465

 

       根据上述源码总结出的ChunkDexClassLookup结构声明:

struct ChunkDexClassLookup{

       Header header;

       DexClassLookup lookup;

}

 

ChunkRegisterMapPool的结构体是writeOptData()函数向writeChunk()函数传递1个RegisterMapBuilder结构体指针。

RegisterMapBuilder结构体通过dvmGenerateRegisterMaps()函数填充。

dvmGenerateRegisterMaps()调用writeMapsAllClasses()填充所有类的映射信息,

writeMapsAllClasses()调用writeMapsAllMethods()填充所有方法映射信息

writeMapsAllMethods()调用writeMapForMethod()依次填充每个方法的映射信息

并调用computeRegisterMapSize()函数计算填充的每个方法映射信息的长度,用来循环遍历所有的方法

struct ChunkRegisterMapPool{

       Header header;

       struct{

              struct RegisterMapClassPool{

                     u4 numClasses;

                     u4 classDataOffset[1];

              }classpool;

              struct RegisterMapMethodPool{

                     u2 methodCount;

                     u4 methodData[1];

              };

       }lookup;

};

      

ChunkEnd结构时,writeOptData()向writeChunk()传递了一个null指针,根据传递的kDexChunkEnd类型来判断。

odex文件最后的8个字节固定为“44 4E 45 41 00 00 00 00”

struct ChunkEnd{

       Header header;

}

 

 

 

 

 

 

 

 

 

 

 

原文地址:https://www.cnblogs.com/heixiang/p/10967142.html