zoukankan      html  css  js  c++  java
  • 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;

    }

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    SpringMVC框架搭建
    java事务的概念
    SpringMVC框架
    JAVA多线程面试题
    MD5加密
    java对象和xml的转换
    eclipse环境配置
    关于枚举类的使用
    定时器的使用
    关于AS-OS
  • 原文地址:https://www.cnblogs.com/heixiang/p/10967142.html
Copyright © 2011-2022 走看看