• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
extractAndProcessZip(int zipFd,int cacheFd,const char * debugFileName,bool isBootstrap,const char * bootClassPath,const char * dexoptFlagStr)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  */
processZipFile(int zipFd,int cacheFd,const char * zipName,const char * dexoptFlags)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  */
fromZip(int argc,char * const argv[])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  */
preopt(int argc,char * const argv[])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  */
fromDex(int argc,char * const argv[])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  */
main(int argc,char * const argv[])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