Home | History | Annotate | Download | only in dexopt
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * Command-line DEX optimization and verification entry point.
     19  *
     20  * There are three ways to launch this:
     21  * (1) From the VM.  This takes a dozen args, one of which is a file
     22  *     descriptor that acts as both input and output.  This allows us to
     23  *     remain ignorant of where the DEX data originally came from.
     24  * (2) From installd or another native application.  Pass in a file
     25  *     descriptor for a zip file, a file descriptor for the output, and
     26  *     a filename for debug messages.  Many assumptions are made about
     27  *     what's going on (verification + optimization are enabled, boot
     28  *     class path is in BOOTCLASSPATH, etc).
     29  * (3) On the host during a build for preoptimization. This behaves
     30  *     almost the same as (2), except it takes file names instead of
     31  *     file descriptors.
     32  *
     33  * There are some fragile aspects around bootclasspath entries, owing
     34  * largely to the VM's history of working on whenever it thought it needed
     35  * instead of strictly doing what it was told.  If optimizing bootclasspath
     36  * entries, always do them in the order in which they appear in the path.
     37  */
     38 #include "Dalvik.h"
     39 #include "libdex/OptInvocation.h"
     40 
     41 #include "cutils/log.h"
     42 #include "cutils/process_name.h"
     43 
     44 #include <fcntl.h>
     45 #include <stdbool.h>
     46 #include <stdlib.h>
     47 #include <stdio.h>
     48 #include <string.h>
     49 
     50 static const char* kClassesDex = "classes.dex";
     51 
     52 
     53 /*
     54  * Extract "classes.dex" from zipFd into "cacheFd", leaving a little space
     55  * up front for the DEX optimization header.
     56  */
     57 static int extractAndProcessZip(int zipFd, int cacheFd,
     58     const char* debugFileName, bool isBootstrap, const char* bootClassPath,
     59     const char* dexoptFlagStr)
     60 {
     61     ZipArchive zippy;
     62     ZipEntry zipEntry;
     63     size_t uncompLen;
     64     long modWhen, crc32;
     65     off_t dexOffset;
     66     int err;
     67     int result = -1;
     68     int dexoptFlags = 0;        /* bit flags, from enum DexoptFlags */
     69     DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
     70     DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
     71 
     72     memset(&zippy, 0, sizeof(zippy));
     73 
     74     /* make sure we're still at the start of an empty file */
     75     if (lseek(cacheFd, 0, SEEK_END) != 0) {
     76         ALOGE("DexOptZ: new cache file '%s' is not empty", debugFileName);
     77         goto bail;
     78     }
     79 
     80     /*
     81      * Write a skeletal DEX optimization header.  We want the classes.dex
     82      * to come just after it.
     83      */
     84     err = dexOptCreateEmptyHeader(cacheFd);
     85     if (err != 0)
     86         goto bail;
     87 
     88     /* record the file position so we can get back here later */
     89     dexOffset = lseek(cacheFd, 0, SEEK_CUR);
     90     if (dexOffset < 0)
     91         goto bail;
     92 
     93     /*
     94      * Open the zip archive, find the DEX entry.
     95      */
     96     if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) {
     97         ALOGW("DexOptZ: unable to open zip archive '%s'", debugFileName);
     98         goto bail;
     99     }
    100 
    101     zipEntry = dexZipFindEntry(&zippy, kClassesDex);
    102     if (zipEntry == NULL) {
    103         ALOGW("DexOptZ: zip archive '%s' does not include %s",
    104             debugFileName, kClassesDex);
    105         goto bail;
    106     }
    107 
    108     /*
    109      * Extract some info about the zip entry.
    110      */
    111     if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL,
    112             &modWhen, &crc32) != 0)
    113     {
    114         ALOGW("DexOptZ: zip archive GetEntryInfo failed on %s", debugFileName);
    115         goto bail;
    116     }
    117 
    118     uncompLen = uncompLen;
    119     modWhen = modWhen;
    120     crc32 = crc32;
    121 
    122     /*
    123      * Extract the DEX data into the cache file at the current offset.
    124      */
    125     if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0) {
    126         ALOGW("DexOptZ: extraction of %s from %s failed",
    127             kClassesDex, debugFileName);
    128         goto bail;
    129     }
    130 
    131     /* Parse the options. */
    132     if (dexoptFlagStr[0] != '\0') {
    133         const char* opc;
    134         const char* val;
    135 
    136         opc = strstr(dexoptFlagStr, "v=");      /* verification */
    137         if (opc != NULL) {
    138             switch (*(opc+2)) {
    139             case 'n':   verifyMode = VERIFY_MODE_NONE;          break;
    140             case 'r':   verifyMode = VERIFY_MODE_REMOTE;        break;
    141             case 'a':   verifyMode = VERIFY_MODE_ALL;           break;
    142             default:                                            break;
    143             }
    144         }
    145 
    146         opc = strstr(dexoptFlagStr, "o=");      /* optimization */
    147         if (opc != NULL) {
    148             switch (*(opc+2)) {
    149             case 'n':   dexOptMode = OPTIMIZE_MODE_NONE;        break;
    150             case 'v':   dexOptMode = OPTIMIZE_MODE_VERIFIED;    break;
    151             case 'a':   dexOptMode = OPTIMIZE_MODE_ALL;         break;
    152             case 'f':   dexOptMode = OPTIMIZE_MODE_FULL;        break;
    153             default:                                            break;
    154             }
    155         }
    156 
    157         opc = strstr(dexoptFlagStr, "m=y");     /* register map */
    158         if (opc != NULL) {
    159             dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
    160         }
    161 
    162         opc = strstr(dexoptFlagStr, "u=");      /* uniprocessor target */
    163         if (opc != NULL) {
    164             switch (*(opc+2)) {
    165             case 'y':   dexoptFlags |= DEXOPT_UNIPROCESSOR;     break;
    166             case 'n':   dexoptFlags |= DEXOPT_SMP;              break;
    167             default:                                            break;
    168             }
    169         }
    170     }
    171 
    172     /*
    173      * Prep the VM and perform the optimization.
    174      */
    175 
    176     if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
    177             dexoptFlags) != 0)
    178     {
    179         ALOGE("DexOptZ: VM init failed");
    180         goto bail;
    181     }
    182 
    183     //vmStarted = 1;
    184 
    185     /* do the optimization */
    186     if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName,
    187             modWhen, crc32, isBootstrap))
    188     {
    189         ALOGE("Optimization failed");
    190         goto bail;
    191     }
    192 
    193     /* we don't shut the VM down -- process is about to exit */
    194 
    195     result = 0;
    196 
    197 bail:
    198     dexZipCloseArchive(&zippy);
    199     return result;
    200 }
    201 
    202 /*
    203  * Common functionality for normal device-side processing as well as
    204  * preoptimization.
    205  */
    206 static int processZipFile(int zipFd, int cacheFd, const char* zipName,
    207         const char *dexoptFlags)
    208 {
    209     char* bcpCopy = NULL;
    210 
    211     /*
    212      * Check to see if this is a bootstrap class entry. If so, truncate
    213      * the path.
    214      */
    215     const char* bcp = getenv("BOOTCLASSPATH");
    216     if (bcp == NULL) {
    217         ALOGE("DexOptZ: BOOTCLASSPATH not set");
    218         return -1;
    219     }
    220 
    221     bool isBootstrap = false;
    222     const char* match = strstr(bcp, zipName);
    223     if (match != NULL) {
    224         /*
    225          * TODO: we have a partial string match, but that doesn't mean
    226          * we've matched an entire path component. We should make sure
    227          * that we're matching on the full zipName, and if not we
    228          * should re-do the strstr starting at (match+1).
    229          *
    230          * The scenario would be a bootclasspath with something like
    231          * "/system/framework/core.jar" while we're trying to optimize
    232          * "/framework/core.jar". Not very likely since all paths are
    233          * absolute and end with ".jar", but not impossible.
    234          */
    235         int matchOffset = match - bcp;
    236         if (matchOffset > 0 && bcp[matchOffset-1] == ':')
    237             matchOffset--;
    238         ALOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d",
    239             zipName, matchOffset);
    240         bcpCopy = strdup(bcp);
    241         bcpCopy[matchOffset] = '\0';
    242 
    243         bcp = bcpCopy;
    244         ALOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'", bcp);
    245         isBootstrap = true;
    246     }
    247 
    248     int result = extractAndProcessZip(zipFd, cacheFd, zipName, isBootstrap,
    249             bcp, dexoptFlags);
    250 
    251     free(bcpCopy);
    252     return result;
    253 }
    254 
    255 /* advance to the next arg and extract it */
    256 #define GET_ARG(_var, _func, _msg)                                          \
    257     {                                                                       \
    258         char* endp;                                                         \
    259         (_var) = _func(*++argv, &endp, 0);                                  \
    260         if (*endp != '\0') {                                                \
    261             ALOGE("%s '%s'", _msg, *argv);                                   \
    262             goto bail;                                                      \
    263         }                                                                   \
    264         --argc;                                                             \
    265     }
    266 
    267 /*
    268  * Parse arguments.  We want:
    269  *   0. (name of dexopt command -- ignored)
    270  *   1. "--zip"
    271  *   2. zip fd (input, read-only)
    272  *   3. cache fd (output, read-write, locked with flock)
    273  *   4. filename of zipfile being optimized (used for debug messages and
    274  *      for comparing against BOOTCLASSPATH; does not need to be
    275  *      accessible or even exist)
    276  *   5. dexopt flags
    277  *
    278  * The BOOTCLASSPATH environment variable is assumed to hold the correct
    279  * boot class path.  If the filename provided appears in the boot class
    280  * path, the path will be truncated just before that entry (so that, if
    281  * you were to dexopt "core.jar", your bootclasspath would be empty).
    282  *
    283  * This does not try to normalize the boot class path name, so the
    284  * filename test won't catch you if you get creative.
    285  */
    286 static int fromZip(int argc, char* const argv[])
    287 {
    288     int result = -1;
    289     int zipFd, cacheFd;
    290     const char* zipName;
    291     char* bcpCopy = NULL;
    292     const char* dexoptFlags;
    293 
    294     if (argc != 6) {
    295         ALOGE("Wrong number of args for --zip (found %d)", argc);
    296         goto bail;
    297     }
    298 
    299     /* skip "--zip" */
    300     argc--;
    301     argv++;
    302 
    303     GET_ARG(zipFd, strtol, "bad zip fd");
    304     GET_ARG(cacheFd, strtol, "bad cache fd");
    305     zipName = *++argv;
    306     --argc;
    307     dexoptFlags = *++argv;
    308     --argc;
    309 
    310     result = processZipFile(zipFd, cacheFd, zipName, dexoptFlags);
    311 
    312 bail:
    313     return result;
    314 }
    315 
    316 /*
    317  * Parse arguments for a preoptimization run. This is when dalvikvm is run
    318  * on a host to optimize dex files for eventual running on a (different)
    319  * device. We want:
    320  *   0. (name of dexopt command -- ignored)
    321  *   1. "--preopt"
    322  *   2. zipfile name
    323  *   3. output file name
    324  *   4. dexopt flags
    325  *
    326  * The BOOTCLASSPATH environment variable is assumed to hold the correct
    327  * boot class path.  If the filename provided appears in the boot class
    328  * path, the path will be truncated just before that entry (so that, if
    329  * you were to dexopt "core.jar", your bootclasspath would be empty).
    330  *
    331  * This does not try to normalize the boot class path name, so the
    332  * filename test won't catch you if you get creative.
    333  */
    334 static int preopt(int argc, char* const argv[])
    335 {
    336     int zipFd = -1;
    337     int outFd = -1;
    338     int result = -1;
    339 
    340     if (argc != 5) {
    341         /*
    342          * Use stderr here, since this variant is meant to be called on
    343          * the host side.
    344          */
    345         fprintf(stderr, "Wrong number of args for --preopt (found %d)\n",
    346                 argc);
    347         return -1;
    348     }
    349 
    350     const char* zipName = argv[2];
    351     const char* outName = argv[3];
    352     const char* dexoptFlags = argv[4];
    353 
    354     if (strstr(dexoptFlags, "u=y") == NULL &&
    355         strstr(dexoptFlags, "u=n") == NULL)
    356     {
    357         fprintf(stderr, "Either 'u=y' or 'u=n' must be specified\n");
    358         return -1;
    359     }
    360 
    361     zipFd = open(zipName, O_RDONLY);
    362     if (zipFd < 0) {
    363         perror(argv[0]);
    364         return -1;
    365     }
    366 
    367     outFd = open(outName, O_RDWR | O_EXCL | O_CREAT, 0666);
    368     if (outFd < 0) {
    369         perror(argv[0]);
    370         goto bail;
    371     }
    372 
    373     result = processZipFile(zipFd, outFd, zipName, dexoptFlags);
    374 
    375 bail:
    376     if (zipFd >= 0) {
    377         close(zipFd);
    378     }
    379 
    380     if (outFd >= 0) {
    381         close(outFd);
    382     }
    383 
    384     return result;
    385 }
    386 
    387 /*
    388  * Parse arguments for an "old-style" invocation directly from the VM.
    389  *
    390  * Here's what we want:
    391  *   0. (name of dexopt command -- ignored)
    392  *   1. "--dex"
    393  *   2. DALVIK_VM_BUILD value, as a sanity check
    394  *   3. file descriptor, locked with flock, for DEX file being optimized
    395  *   4. DEX offset within file
    396  *   5. DEX length
    397  *   6. filename of file being optimized (for debug messages only)
    398  *   7. modification date of source (goes into dependency section)
    399  *   8. CRC of source (goes into dependency section)
    400  *   9. flags (optimization level, isBootstrap)
    401  *  10. bootclasspath entry #1
    402  *  11. bootclasspath entry #2
    403  *   ...
    404  *
    405  * dvmOptimizeDexFile() in dalvik/vm/analysis/DexOptimize.c builds the
    406  * argument list and calls this executable.
    407  *
    408  * The bootclasspath entries become the dependencies for this DEX file.
    409  *
    410  * The open file descriptor MUST NOT be for one of the bootclasspath files.
    411  * The parent has the descriptor locked, and we'll try to lock it again as
    412  * part of processing the bootclasspath.  (We can catch this and return
    413  * an error by comparing filenames or by opening the bootclasspath files
    414  * and stat()ing them for inode numbers).
    415  */
    416 static int fromDex(int argc, char* const argv[])
    417 {
    418     int result = -1;
    419     bool vmStarted = false;
    420     char* bootClassPath = NULL;
    421     int fd, flags, vmBuildVersion;
    422     long offset, length;
    423     const char* debugFileName;
    424     u4 crc, modWhen;
    425     char* endp;
    426     bool onlyOptVerifiedDex = false;
    427     DexClassVerifyMode verifyMode;
    428     DexOptimizerMode dexOptMode;
    429 
    430     if (argc < 10) {
    431         /* don't have all mandatory args */
    432         ALOGE("Not enough arguments for --dex (found %d)", argc);
    433         goto bail;
    434     }
    435 
    436     /* skip "--dex" */
    437     argc--;
    438     argv++;
    439 
    440     /*
    441      * Extract the args.
    442      */
    443     GET_ARG(vmBuildVersion, strtol, "bad vm build");
    444     if (vmBuildVersion != DALVIK_VM_BUILD) {
    445         ALOGE("DexOpt: build rev does not match VM: %d vs %d",
    446             vmBuildVersion, DALVIK_VM_BUILD);
    447         goto bail;
    448     }
    449     GET_ARG(fd, strtol, "bad fd");
    450     GET_ARG(offset, strtol, "bad offset");
    451     GET_ARG(length, strtol, "bad length");
    452     debugFileName = *++argv;
    453     --argc;
    454     GET_ARG(modWhen, strtoul, "bad modWhen");
    455     GET_ARG(crc, strtoul, "bad crc");
    456     GET_ARG(flags, strtol, "bad flags");
    457 
    458     ALOGV("Args: fd=%d off=%ld len=%ld name='%s' mod=%#x crc=%#x flg=%d (argc=%d)",
    459         fd, offset, length, debugFileName, modWhen, crc, flags, argc);
    460     assert(argc > 0);
    461 
    462     if (--argc == 0) {
    463         bootClassPath = strdup("");
    464     } else {
    465         int i, bcpLen;
    466         char* const* argp;
    467         char* cp;
    468 
    469         bcpLen = 0;
    470         for (i = 0, argp = argv; i < argc; i++) {
    471             ++argp;
    472             ALOGV("DEP: '%s'", *argp);
    473             bcpLen += strlen(*argp) + 1;
    474         }
    475 
    476         cp = bootClassPath = (char*) malloc(bcpLen +1);
    477         for (i = 0, argp = argv; i < argc; i++) {
    478             int strLen;
    479 
    480             ++argp;
    481             strLen = strlen(*argp);
    482             if (i != 0)
    483                 *cp++ = ':';
    484             memcpy(cp, *argp, strLen);
    485             cp += strLen;
    486         }
    487         *cp = '\0';
    488 
    489         assert((int) strlen(bootClassPath) == bcpLen-1);
    490     }
    491     ALOGV("  bootclasspath is '%s'", bootClassPath);
    492 
    493     /* start the VM partway */
    494 
    495     /* ugh -- upgrade these to a bit field if they get any more complex */
    496     if ((flags & DEXOPT_VERIFY_ENABLED) != 0) {
    497         if ((flags & DEXOPT_VERIFY_ALL) != 0)
    498             verifyMode = VERIFY_MODE_ALL;
    499         else
    500             verifyMode = VERIFY_MODE_REMOTE;
    501     } else {
    502         verifyMode = VERIFY_MODE_NONE;
    503     }
    504     if ((flags & DEXOPT_OPT_ENABLED) != 0) {
    505         if ((flags & DEXOPT_OPT_ALL) != 0)
    506             dexOptMode = OPTIMIZE_MODE_ALL;
    507         else
    508             dexOptMode = OPTIMIZE_MODE_VERIFIED;
    509     } else {
    510         dexOptMode = OPTIMIZE_MODE_NONE;
    511     }
    512 
    513     if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) != 0) {
    514         ALOGE("VM init failed");
    515         goto bail;
    516     }
    517 
    518     vmStarted = true;
    519 
    520     /* do the optimization */
    521     if (!dvmContinueOptimization(fd, offset, length, debugFileName,
    522             modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
    523     {
    524         ALOGE("Optimization failed");
    525         goto bail;
    526     }
    527 
    528     result = 0;
    529 
    530 bail:
    531     /*
    532      * In theory we should gracefully shut the VM down at this point.  In
    533      * practice that only matters if we're checking for memory leaks with
    534      * valgrind -- simply exiting is much faster.
    535      *
    536      * As it turns out, the DEX optimizer plays a little fast and loose
    537      * with class loading.  We load all of the classes from a partially-
    538      * formed DEX file, which is unmapped when we're done.  If we want to
    539      * do clean shutdown here, perhaps for testing with valgrind, we need
    540      * to skip the munmap call there.
    541      */
    542 #if 0
    543     if (vmStarted) {
    544         ALOGI("DexOpt shutting down, result=%d", result);
    545         dvmShutdown();
    546     }
    547 #endif
    548 
    549     free(bootClassPath);
    550     ALOGV("DexOpt command complete (result=%d)", result);
    551     return result;
    552 }
    553 
    554 /*
    555  * Main entry point.  Decide where to go.
    556  */
    557 int main(int argc, char* const argv[])
    558 {
    559     set_process_name("dexopt");
    560 
    561     setvbuf(stdout, NULL, _IONBF, 0);
    562 
    563     if (argc > 1) {
    564         if (strcmp(argv[1], "--zip") == 0)
    565             return fromZip(argc, argv);
    566         else if (strcmp(argv[1], "--dex") == 0)
    567             return fromDex(argc, argv);
    568         else if (strcmp(argv[1], "--preopt") == 0)
    569             return preopt(argc, argv);
    570     }
    571 
    572     fprintf(stderr,
    573         "Usage:\n\n"
    574         "Short version: Don't use this.\n\n"
    575         "Slightly longer version: This system-internal tool is used to\n"
    576         "produce optimized dex files. See the source code for details.\n");
    577 
    578     return 1;
    579 }
    580