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

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

文章目录

前言

一、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 文件 ;



/*
 * 给定包含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+'\0' -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=0x%04x, process failed",
                lastPart, status);
            return false;
        }
    }
}





二、/bin/dexopt 源码分析


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


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


image.png


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


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


return fromDex(argc, argv);


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


 

if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) != 0) {
        ALOGE("VM init failed");
        goto bail;
    }


然后进行正式优化 ;


/* 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


/*
 * 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] != '\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] = '\0';
        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 != '\0') {                                                \
            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 specified\n");
        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 = '\0';
        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:\n\n"
        "Short version: Don't use this.\n\n"
        "Slightly longer version: This system-internal tool is used to\n"
        "produce optimized dex files. See the source code for details.\n");
    return 1;
}



目录
相关文章
|
26天前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
105 4
|
18天前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
24 8
|
22天前
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
30 1
|
缓存 Android开发 数据格式
Android ListView性能优化,异步加载图片
版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/48184383 ListView性能优...
1172 0
|
缓存 算法 Android开发
Android 性能优化——之图片的优化
Android 性能优化——之图片的优化  在Android性能优化中,我们会发现占内存最大的和对性能影响最大的往往是图片资源,其次是控件资源。相对来说,其他的资源的影响会小一点。这里我就先对图片资源的优化进行一下讲解,如果有什么说的不对的,希望大神指正一下。
1084 0
|
27天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
14天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19
|
27天前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
14天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
41 14