【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )

2023-03-30 09:26:49 浏览数 (1)

文章目录

  • 前言
  • 一、DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析
  • 二、/bin/dexopt 源码分析

前言


上一篇博客 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | RawDexFile.cpp 分析 | dvmRawDexFileOpen函数读取 DEX 文件 ) 中 , 在 RawDexFile.cpp 中的 dvmRawDexFileOpen() 方法中 , 调用了 DexPrepare.cpp 的 dvmOptimizeDexFile() 函数 , 对 DEX 文件进行了优化 ;

一、DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析


dvmOptimizeDexFile 函数的参数说明 : int fd 是打开的 dex 文件标识符 , long dexLength 是打开的 dex 文件大小 ;

在该函数中 , 调用 /bin/dexopt 程序 , 优化 dex 文件 , 最终产生 odex 文件 ;

代码语言:javascript复制
/*
 * 给定包含DEX数据的文件的描述符,生成
 * 优化版本。
 * 
 * “fd”指向的文件应为锁定的共享资源
 * (或私人);我们不努力实施多进程正确性
 * 在这里。
 * 
 * “文件名”仅用于调试输出。存储“modWhen”和“crc”
 * 在依赖项集中。
 * 
 * “isBootstrap”标志确定优化器和验证器如何处理
 * 包范围访问检查。优化时,我们只加载引导
 * 类DEX文件和目标DEX,因此该标志确定
 * 给目标DEX类一个(合成的)非空类加载器指针。
 * 只有当目标DEX包含声明
 * 与引导类位于同一个包中。
 * 
 * 优化器需要加载目标DEX文件中的每个类。
 * 这通常是不可取的,因此我们启动一个子流程来执行
 * 工作并等待它完成。
 * 
 * 成功时返回“true”。所有数据均已写入“fd”。
 */
bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
{
    const char* lastPart = strrchr(fileName, '/');
    if (lastPart != NULL)
        lastPart  ;
    else
        lastPart = fileName;

    ALOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---", lastPart, isBootstrap);

    pid_t pid;

	/*
	 * 如果我们的bootclasspath中出现了我们认为
	 * 都优化了,被拒绝了。
	 */
    if (gDvm.optimizing) {
        ALOGW("Rejecting recursive optimization attempt on '%s'", fileName);
        return false;
    }

    pid = fork();
    if (pid == 0) {
        static const int kUseValgrind = 0;
        // 调用 /bin/dexopt 程序 , 优化 dex 文件 , 最终产生 odex 文件
        static const char* kDexOptBin = "/bin/dexopt";
        static const char* kValgrinder = "/usr/bin/valgrind";
        static const int kFixedArgCount = 10;
        static const int kValgrindArgCount = 5;
        static const int kMaxIntLen = 12;   // '-' 10dig '' -OR- 0x 8dig
        int bcpSize = dvmGetBootPathSize();
        int argc = kFixedArgCount   bcpSize
              (kValgrindArgCount * kUseValgrind);
        const char* argv[argc 1];             // last entry is NULL
        char values[argc][kMaxIntLen];
        char* execFile;
        const char* androidRoot;
        int flags;

        /* change process groups, so we don't clash with ProcessManager */
        setpgid(0, 0);

        /* full path to optimizer */
        androidRoot = getenv("ANDROID_ROOT");
        if (androidRoot == NULL) {
            ALOGW("ANDROID_ROOT not set, defaulting to /system");
            androidRoot = "/system";
        }
        execFile = (char*)alloca(strlen(androidRoot)   strlen(kDexOptBin)   1);
        strcpy(execFile, androidRoot);
        strcat(execFile, kDexOptBin);

        /*
         * Create arg vector.
         */
        int curArg = 0;

        if (kUseValgrind) {
            /* probably shouldn't ship the hard-coded path */
            argv[curArg  ] = (char*)kValgrinder;
            argv[curArg  ] = "--tool=memcheck";
            argv[curArg  ] = "--leak-check=yes";        // check for leaks too
            argv[curArg  ] = "--leak-resolution=med";   // increase from 2 to 4
            argv[curArg  ] = "--num-callers=16";        // default is 12
            assert(curArg == kValgrindArgCount);
        }
        argv[curArg  ] = execFile;

        argv[curArg  ] = "--dex";

        sprintf(values[2], "%d", DALVIK_VM_BUILD);
        argv[curArg  ] = values[2];

        sprintf(values[3], "%d", fd);
        argv[curArg  ] = values[3];

        sprintf(values[4], "%d", (int) dexOffset);
        argv[curArg  ] = values[4];

        sprintf(values[5], "%d", (int) dexLength);
        argv[curArg  ] = values[5];

        argv[curArg  ] = (char*)fileName;

        sprintf(values[7], "%d", (int) modWhen);
        argv[curArg  ] = values[7];

        sprintf(values[8], "%d", (int) crc);
        argv[curArg  ] = values[8];

        flags = 0;
        if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE) {
            flags |= DEXOPT_OPT_ENABLED;
            if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
                flags |= DEXOPT_OPT_ALL;
        }
        if (gDvm.classVerifyMode != VERIFY_MODE_NONE) {
            flags |= DEXOPT_VERIFY_ENABLED;
            if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
                flags |= DEXOPT_VERIFY_ALL;
        }
        if (isBootstrap)
            flags |= DEXOPT_IS_BOOTSTRAP;
        if (gDvm.generateRegisterMaps)
            flags |= DEXOPT_GEN_REGISTER_MAPS;
        sprintf(values[9], "%d", flags);
        argv[curArg  ] = values[9];

        assert(((!kUseValgrind && curArg == kFixedArgCount) ||
               ((kUseValgrind && curArg == kFixedArgCount kValgrindArgCount))));

        ClassPathEntry* cpe;
        for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe  ) {
            argv[curArg  ] = cpe->fileName;
        }
        assert(curArg == argc);

        argv[curArg] = NULL;

        if (kUseValgrind)
            execv(kValgrinder, const_cast<char**>(argv));
        else
            execv(execFile, const_cast<char**>(argv));

        ALOGE("execv '%s'%s failed: %s", execFile,
            kUseValgrind ? " [valgrind]" : "", strerror(errno));
        exit(1);
    } else {
        ALOGV("DexOpt: waiting for verify opt, pid=%d", (int) pid);
        int status;
        pid_t gotPid;

		/*
		 * 等待优化过程完成。我们进入VMI等待
		 * 模式,这样GC暂停就不必等待我们了。
		 */
        ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
        while (true) {
            gotPid = waitpid(pid, &status, 0);
            if (gotPid == -1 && errno == EINTR) {
                ALOGD("waitpid interrupted, retrying");
            } else {
                break;
            }
        }
        dvmChangeStatus(NULL, oldStatus);
        if (gotPid != pid) {
            ALOGE("waitpid failed: wanted %d, got %d: %s",
                (int) pid, (int) gotPid, strerror(errno));
            return false;
        }

        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
            ALOGD("DexOpt: --- END '%s' (success) ---", lastPart);
            return true;
        } else {
            ALOGW("DexOpt: --- END '%s' --- status=0xx, process failed",
                lastPart, status);
            return false;
        }
    }
}

二、/bin/dexopt 源码分析


dex 文件优化 , 主要是调用 /bin/dexopt 程序 , 最终产生 odex 文件 ;

其源码路径是 /dalvik/dexopt/ 路径 ,

该 OptMain.cpp 源码是一个有 main 函数 , 可以独立执行的 C 程序 , 可以在 Android 命令中执行 ;

加载 dex 文件时 , 执行 fromDex 函数 ;

代码语言:javascript复制
return fromDex(argc, argv);

在 fromfromDex 函数中 , 先准备优化环境 ,

代码语言:javascript复制
    if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) != 0) {
        ALOGE("VM init failed");
        goto bail;
    }

然后进行正式优化 ;

代码语言:javascript复制
    /* do the optimization */
    if (!dvmContinueOptimization(fd, offset, length, debugFileName,
            modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
    {
        ALOGE("Optimization failed");
        goto bail;
    }

真正的优化操作 , 在 dvmContinueOptimization 函数中执行的 ;

核心源码如下 : 源码路径 /dalvik/dexopt/OptMain.cpp

代码语言:javascript复制
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * 命令行DEX优化和验证入口点。
 * 
 * 有三种方法可以启动此功能:
 * (1)来自虚拟机。这需要十几个参数,其中一个是文件
 * 同时作为输入和输出的描述符。这使我们能够
 * 仍然不知道DEX数据最初来自何处。
 * (2)来自installd或其他本机应用程序。传入文件
 * 用于zip文件的描述符、用于输出的文件描述符,以及
 * 调试消息的文件名。关于这一点,人们做了许多假设
 * 发生了什么(验证 优化已启用,启动
 * 类路径位于BOOTCLASSPATH中,等等)。
 * (3)在构建过程中在主机上进行预优化。这种行为
 * 与(2)几乎相同,只是它采用文件名而不是
 * 文件描述符。
 * 
 * bootclasspath条目存在一些脆弱的方面,原因如下
 * 很大程度上是由于虚拟机在它认为需要的时候进行工作的历史
 * 而不是严格按照要求去做。如果优化引导类路径
 * 条目,始终按照它们在路径中出现的顺序执行。
 */
#include "Dalvik.h"
#include "libdex/OptInvocation.h"

#include "cutils/log.h"
#include "cutils/process_name.h"

#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

static const char* kClassesDex = "classes.dex";


/*
*将zipFd中的“classes.dex”提取到“cacheFd”中,留下一点空间
*用于DEX优化收割台的前端。
*/
static int extractAndProcessZip(int zipFd, int cacheFd,
    const char* debugFileName, bool isBootstrap, const char* bootClassPath,
    const char* dexoptFlagStr)
{
    ZipArchive zippy;
    ZipEntry zipEntry;
    size_t uncompLen;
    long modWhen, crc32;
    off_t dexOffset;
    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;
    }

	/*
	*编写骨架索引优化标头。我们要上课。指数
	*紧跟其后。
	*/
    err = dexOptCreateEmptyHeader(cacheFd);
    if (err != 0)
        goto bail;

    /* record the file position so we can get back here later */
    dexOffset = lseek(cacheFd, 0, SEEK_CUR);
    if (dexOffset < 0)
        goto bail;

	/*
	*打开zip存档,找到DEX条目。
	*/
    if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) {
        ALOGW("DexOptZ: unable to open zip archive '%s'", debugFileName);
        goto bail;
    }

    zipEntry = dexZipFindEntry(&zippy, kClassesDex);
    if (zipEntry == NULL) {
        ALOGW("DexOptZ: zip archive '%s' does not include %s",
            debugFileName, kClassesDex);
        goto bail;
    }

	/*
	*提取一些关于zip条目的信息。
	*/
    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;

	/*
	*以当前偏移量将DEX数据提取到缓存文件中。
	*/
    if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0) {
        ALOGW("DexOptZ: extraction of %s from %s failed",
            kClassesDex, debugFileName);
        goto bail;
    }

    /* Parse the options. */
    if (dexoptFlagStr[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;
            }
        }
    }

	/*
	*准备VM并执行优化。
	*/

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

/*
*普通设备端处理的通用功能以及
*预优化。
*/
static int processZipFile(int zipFd, int cacheFd, const char* zipName,
        const char *dexoptFlags)
{
    char* bcpCopy = NULL;

    /*
     * Check to see if this is a bootstrap class entry. If so, truncate
     * the path.
     */
    const char* bcp = getenv("BOOTCLASSPATH");
    if (bcp == NULL) {
        ALOGE("DexOptZ: BOOTCLASSPATH not set");
        return -1;
    }

    bool isBootstrap = false;
    const char* match = strstr(bcp, zipName);
    if (match != NULL) {
		/*
		*TODO:我们有一个部分字符串匹配,但这并不意味着
		*我们已经匹配了整个路径组件。我们应该确保
		*我们正在匹配完整的zipName,如果不是
		*应从(匹配 1)开始重新执行strstr。
		*
		*该场景将是一个bootclasspath,具有以下内容
		*“/system/framework/core.jar”,而我们正在尝试优化
		*“/framework/core.jar”。不太可能,因为所有路径都是
		*绝对,以“.jar”结尾,但并非不可能。
		*/
        int matchOffset = match - bcp;
        if (matchOffset > 0 && bcp[matchOffset-1] == ':')
            matchOffset--;
        ALOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d",
            zipName, matchOffset);
        bcpCopy = strdup(bcp);
        bcpCopy[matchOffset] = '';

        bcp = bcpCopy;
        ALOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'", bcp);
        isBootstrap = true;
    }

    int result = extractAndProcessZip(zipFd, cacheFd, zipName, isBootstrap,
            bcp, dexoptFlags);

    free(bcpCopy);
    return result;
}

/* advance to the next arg and extract it */
#define GET_ARG(_var, _func, _msg)                                          
    {                                                                       
        char* endp;                                                         
        (_var) = _func(*  argv, &endp, 0);                                  
        if (*endp != '') {                                                
            ALOGE("%s '%s'", _msg, *argv);                                   
            goto bail;                                                      
        }                                                                   
        --argc;                                                             
    }

/*
*解析参数。我们希望:
*   0. (dexopt命令的名称--已忽略)
*   1. “--zip”
*   2. zip fd(输入,只读)
*   3. 缓存fd(输出、读写、用群集锁定)
*   4. 正在优化的zipfile的文件名(用于调试消息和
*用于与BOOTCLASSPATH进行比较;不需要
*可访问或甚至存在)
*   5. dexopt标志
*
*假定BOOTCLASSPATH环境变量包含正确的
*引导类路径。如果提供的文件名出现在引导类中
*路径,路径将在该条目之前被截断(因此,如果
*如果您选择dexopt“core.jar”,您的引导类路径将为空)。
*
*这不会尝试规范化引导类路径名,因此
*如果你有创意,文件名测试不会抓住你。
*/
static int fromZip(int argc, char* const argv[])
{
    int result = -1;
    int zipFd, cacheFd;
    const char* zipName;
    char* bcpCopy = NULL;
    const char* dexoptFlags;

    if (argc != 6) {
        ALOGE("Wrong number of args for --zip (found %d)", argc);
        goto bail;
    }

    /* skip "--zip" */
    argc--;
    argv  ;

    GET_ARG(zipFd, strtol, "bad zip fd");
    GET_ARG(cacheFd, strtol, "bad cache fd");
    zipName = *  argv;
    --argc;
    dexoptFlags = *  argv;
    --argc;

    result = processZipFile(zipFd, cacheFd, zipName, dexoptFlags);

bail:
    return result;
}

/*
*分析预优化运行的参数。这是dalvikvm运行的时间
*在主机上优化dex文件,以便最终在主机上运行(不同)
*装置。我们希望:
*   0. (dexopt命令的名称--已忽略)
*   1. “--preopt”
*   2. zipfile名称
*   3. 输出文件名
*   4. dexopt标志
*
*假定BOOTCLASSPATH环境变量包含正确的
*引导类路径。如果提供的文件名出现在引导类中
*路径,路径将在该条目之前被截断(因此,如果
*如果您选择dexopt“core.jar”,您的引导类路径将为空)。
*
*这不会尝试规范化引导类路径名,因此
*如果你有创意,文件名测试不会抓住你。
*/
static int preopt(int argc, char* const argv[])
{
    int zipFd = -1;
    int outFd = -1;
    int result = -1;

    if (argc != 5) {
        /*
         * Use stderr here, since this variant is meant to be called on
         * the host side.
         */
        fprintf(stderr, "Wrong number of args for --preopt (found %d)n",
                argc);
        return -1;
    }

    const char* zipName = argv[2];
    const char* outName = argv[3];
    const char* dexoptFlags = argv[4];

    if (strstr(dexoptFlags, "u=y") == NULL &&
        strstr(dexoptFlags, "u=n") == NULL)
    {
        fprintf(stderr, "Either 'u=y' or 'u=n' must be specifiedn");
        return -1;
    }

    zipFd = open(zipName, O_RDONLY);
    if (zipFd < 0) {
        perror(argv[0]);
        return -1;
    }

    outFd = open(outName, O_RDWR | O_EXCL | O_CREAT, 0666);
    if (outFd < 0) {
        perror(argv[0]);
        goto bail;
    }

    result = processZipFile(zipFd, outFd, zipName, dexoptFlags);

bail:
    if (zipFd >= 0) {
        close(zipFd);
    }

    if (outFd >= 0) {
        close(outFd);
    }

    return result;
}

/*
*直接从VM解析“旧式”调用的参数。
*
*以下是我们想要的:
*   0. (dexopt命令的名称--已忽略)
*   1. “--dex”
*   2. DALVIK_VM_构建值,作为一种健全性检查
*   3. 文件描述符,用flock锁定,用于正在优化的DEX文件
*   4. 文件内的索引偏移量
*   5. 指数长度
*   6. 正在优化的文件的文件名(仅适用于调试消息)
*   7. 源的修改日期(进入依赖项部分)
*   8. 源的CRC(进入依赖项部分)
*   9. 标志(优化级别,isBootstrap)
*  10. bootclasspath条目#1
*  11. bootclasspath条目#2
*   ...
*
*dalvik/vm/analysis/DexOptimize中的dvmOptimizeDexFile()。c构建
*参数列表并调用此可执行文件。
*
*bootclasspath条目将成为此DEX文件的依赖项。
*
*打开的文件描述符不能用于任何bootclasspath文件。
*父项已锁定描述符,我们将尝试再次将其锁定
*处理引导类路径的一部分。(我们可以抓住这个然后回来
*比较文件名或打开bootclasspath文件时出错
*并统计它们的索引节点编号)。
*/
static int fromDex(int argc, char* const argv[])
{
    int result = -1;
    bool vmStarted = false;
    char* bootClassPath = NULL;
    int fd, flags, vmBuildVersion;
    long offset, length;
    const char* debugFileName;
    u4 crc, modWhen;
    char* endp;
    bool onlyOptVerifiedDex = false;
    DexClassVerifyMode verifyMode;
    DexOptimizerMode dexOptMode;

    if (argc < 10) {
        /* don't have all mandatory args */
        ALOGE("Not enough arguments for --dex (found %d)", argc);
        goto bail;
    }

    /* skip "--dex" */
    argc--;
    argv  ;

    /*
     * Extract the args.
     */
    GET_ARG(vmBuildVersion, strtol, "bad vm build");
    if (vmBuildVersion != DALVIK_VM_BUILD) {
        ALOGE("DexOpt: build rev does not match VM: %d vs %d",
            vmBuildVersion, DALVIK_VM_BUILD);
        goto bail;
    }
    GET_ARG(fd, strtol, "bad fd");
    GET_ARG(offset, strtol, "bad offset");
    GET_ARG(length, strtol, "bad length");
    debugFileName = *  argv;
    --argc;
    GET_ARG(modWhen, strtoul, "bad modWhen");
    GET_ARG(crc, strtoul, "bad crc");
    GET_ARG(flags, strtol, "bad flags");

    ALOGV("Args: fd=%d off=%ld len=%ld name='%s' mod=%#x crc=%#x flg=%d (argc=%d)",
        fd, offset, length, debugFileName, modWhen, crc, flags, argc);
    assert(argc > 0);

    if (--argc == 0) {
        bootClassPath = strdup("");
    } else {
        int i, bcpLen;
        char* const* argp;
        char* cp;

        bcpLen = 0;
        for (i = 0, argp = argv; i < argc; i  ) {
              argp;
            ALOGV("DEP: '%s'", *argp);
            bcpLen  = strlen(*argp)   1;
        }

        cp = bootClassPath = (char*) malloc(bcpLen  1);
        for (i = 0, argp = argv; i < argc; i  ) {
            int strLen;

              argp;
            strLen = strlen(*argp);
            if (i != 0)
                *cp   = ':';
            memcpy(cp, *argp, strLen);
            cp  = strLen;
        }
        *cp = '';

        assert((int) strlen(bootClassPath) == bcpLen-1);
    }
    ALOGV("  bootclasspath is '%s'", bootClassPath);

    /* start the VM partway */

    /* ugh -- upgrade these to a bit field if they get any more complex */
    if ((flags & DEXOPT_VERIFY_ENABLED) != 0) {
        if ((flags & DEXOPT_VERIFY_ALL) != 0)
            verifyMode = VERIFY_MODE_ALL;
        else
            verifyMode = VERIFY_MODE_REMOTE;
    } else {
        verifyMode = VERIFY_MODE_NONE;
    }
    if ((flags & DEXOPT_OPT_ENABLED) != 0) {
        if ((flags & DEXOPT_OPT_ALL) != 0)
            dexOptMode = OPTIMIZE_MODE_ALL;
        else
            dexOptMode = OPTIMIZE_MODE_VERIFIED;
    } else {
        dexOptMode = OPTIMIZE_MODE_NONE;
    }

	// 准备优化环境 
    if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) != 0) {
        ALOGE("VM init failed");
        goto bail;
    }

    vmStarted = true;

    /* 正式进行优化 */
    if (!dvmContinueOptimization(fd, offset, length, debugFileName,
            modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
    {
        ALOGE("Optimization failed");
        goto bail;
    }

    result = 0;

bail:
	/*
	*理论上,此时我们应该优雅地关闭VM。在里面
	*只有当我们使用检查内存泄漏时,这才有意义
	*valgrind——简单地退出要快得多。
	*
	*事实证明,DEX优化器有点快,有点松
	*使用类加载。我们从一个部分-
	*形成的DEX文件,完成后将取消映射。如果我们想
	*在这里进行清洁关机,可能是为了使用valgrind进行测试,我们需要
	*要跳过那里的munmap调用。
	*/
#if 0
    if (vmStarted) {
        ALOGI("DexOpt shutting down, result=%d", result);
        dvmShutdown();
    }
#endif

    free(bootClassPath);
    ALOGV("DexOpt command complete (result=%d)", result);
    return result;
}

/*
*主要入口点。决定去哪里。
*/
int main(int argc, char* const argv[])
{
    set_process_name("dexopt");

    setvbuf(stdout, NULL, _IONBF, 0);

    if (argc > 1) {
        if (strcmp(argv[1], "--zip") == 0)
            return fromZip(argc, argv);
        else if (strcmp(argv[1], "--dex") == 0)
        	// 加载 dex 文件时 , 执行 fromDex 函数
            return fromDex(argc, argv);
        else if (strcmp(argv[1], "--preopt") == 0)
            return preopt(argc, argv);
    }

    fprintf(stderr,
        "Usage:nn"
        "Short version: Don't use this.nn"
        "Slightly longer version: This system-internal tool is used ton"
        "produce optimized dex files. See the source code for details.n");

    return 1;
}

源码路径 : /dalvik/dexopt/OptMain.cpp

0 人点赞