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 "utils/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
69 memset(&zippy, 0, sizeof(zippy));
70
71 /* make sure we're still at the start of an empty file */
72 if (lseek(cacheFd, 0, SEEK_END) != 0) {
73 LOGE("DexOptZ: new cache file '%s' is not empty\n", debugFileName);
74 goto bail;
75 }
76
77 /*
78 * Write a skeletal DEX optimization header. We want the classes.dex
79 * to come just after it.
80 */
81 err = dexOptCreateEmptyHeader(cacheFd);
82 if (err != 0)
83 goto bail;
84
85 /* record the file position so we can get back here later */
86 dexOffset = lseek(cacheFd, 0, SEEK_CUR);
87 if (dexOffset < 0)
88 goto bail;
89
90 /*
91 * Open the zip archive, find the DEX entry.
92 */
93 if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) {
94 LOGW("DexOptZ: unable to open zip archive '%s'\n", debugFileName);
95 goto bail;
96 }
97
98 zipEntry = dexZipFindEntry(&zippy, kClassesDex);
99 if (zipEntry == NULL) {
100 LOGW("DexOptZ: zip archive '%s' does not include %s\n",
101 debugFileName, kClassesDex);
102 goto bail;
103 }
104
105 /*
106 * Extract some info about the zip entry.
107 */
108 if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL,
109 &modWhen, &crc32) != 0)
110 {
111 LOGW("DexOptZ: zip archive GetEntryInfo failed on %s\n", debugFileName);
112 goto bail;
113 }
114
115 uncompLen = uncompLen;
116 modWhen = modWhen;
117 crc32 = crc32;
118
119 /*
120 * Extract the DEX data into the cache file at the current offset.
121 */
122 if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0) {
123 LOGW("DexOptZ: extraction of %s from %s failed\n",
124 kClassesDex, debugFileName);
125 goto bail;
126 }
127
128 /* Parse the options. */
129
130 DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
131 DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
132 int dexoptFlags = 0; /* bit flags, from enum DexoptFlags */
133
134 if (dexoptFlagStr[0] != '\0') {
135 const char* opc;
136 const char* val;
137
138 opc = strstr(dexoptFlagStr, "v="); /* verification */
139 if (opc != NULL) {
140 switch (*(opc+2)) {
141 case 'n': verifyMode = VERIFY_MODE_NONE; break;
142 case 'r': verifyMode = VERIFY_MODE_REMOTE; break;
143 case 'a': verifyMode = VERIFY_MODE_ALL; break;
144 default: break;
145 }
146 }
147
148 opc = strstr(dexoptFlagStr, "o="); /* optimization */
149 if (opc != NULL) {
150 switch (*(opc+2)) {
151 case 'n': dexOptMode = OPTIMIZE_MODE_NONE; break;
152 case 'v': dexOptMode = OPTIMIZE_MODE_VERIFIED; break;
153 case 'a': dexOptMode = OPTIMIZE_MODE_ALL; break;
154 default: break;
155 }
156 }
157
158 opc = strstr(dexoptFlagStr, "m=y"); /* register map */
159 if (opc != NULL) {
160 dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
161 }
162
163 opc = strstr(dexoptFlagStr, "u="); /* uniprocessor target */
164 if (opc != NULL) {
165 switch (*(opc+2)) {
166 case 'y': dexoptFlags |= DEXOPT_UNIPROCESSOR; break;
167 case 'n': dexoptFlags |= DEXOPT_SMP; break;
168 default: break;
169 }
170 }
171 }
172
173 /*
174 * Prep the VM and perform the optimization.
175 */
176
177 if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
178 dexoptFlags) != 0)
179 {
180 LOGE("DexOptZ: VM init failed\n");
181 goto bail;
182 }
183
184 //vmStarted = 1;
185
186 /* do the optimization */
187 if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName,
188 modWhen, crc32, isBootstrap))
189 {
190 LOGE("Optimization failed\n");
191 goto bail;
192 }
193
194 /* we don't shut the VM down -- process is about to exit */
195
196 result = 0;
197
198 bail:
199 dexZipCloseArchive(&zippy);
200 return result;
201 }
202
203 /*
204 * Common functionality for normal device-side processing as well as
205 * preoptimization.
206 */
processZipFile(int zipFd,int cacheFd,const char * zipName,const char * dexoptFlags)207 static int processZipFile(int zipFd, int cacheFd, const char* zipName,
208 const char *dexoptFlags)
209 {
210 int result = -1;
211 char* bcpCopy = NULL;
212
213 /*
214 * Check to see if this is a bootstrap class entry. If so, truncate
215 * the path.
216 */
217 const char* bcp = getenv("BOOTCLASSPATH");
218 if (bcp == NULL) {
219 LOGE("DexOptZ: BOOTCLASSPATH not set\n");
220 goto bail;
221 }
222
223 bool isBootstrap = false;
224 const char* match = strstr(bcp, zipName);
225 if (match != NULL) {
226 /*
227 * TODO: we have a partial string match, but that doesn't mean
228 * we've matched an entire path component. We should make sure
229 * that we're matching on the full zipName, and if not we
230 * should re-do the strstr starting at (match+1).
231 *
232 * The scenario would be a bootclasspath with something like
233 * "/system/framework/core.jar" while we're trying to optimize
234 * "/framework/core.jar". Not very likely since all paths are
235 * absolute and end with ".jar", but not impossible.
236 */
237 int matchOffset = match - bcp;
238 if (matchOffset > 0 && bcp[matchOffset-1] == ':')
239 matchOffset--;
240 LOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d\n",
241 inputFileName, matchOffset);
242 bcpCopy = strdup(bcp);
243 bcpCopy[matchOffset] = '\0';
244
245 bcp = bcpCopy;
246 LOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'\n", bcp);
247 isBootstrap = true;
248 }
249
250 result = extractAndProcessZip(zipFd, cacheFd, zipName, isBootstrap,
251 bcp, dexoptFlags);
252
253 bail:
254 free(bcpCopy);
255 return result;
256 }
257
258 /* advance to the next arg and extract it */
259 #define GET_ARG(_var, _func, _msg) \
260 { \
261 char* endp; \
262 (_var) = _func(*++argv, &endp, 0); \
263 if (*endp != '\0') { \
264 LOGE("%s '%s'", _msg, *argv); \
265 goto bail; \
266 } \
267 --argc; \
268 }
269
270 /*
271 * Parse arguments. We want:
272 * 0. (name of dexopt command -- ignored)
273 * 1. "--zip"
274 * 2. zip fd (input, read-only)
275 * 3. cache fd (output, read-write, locked with flock)
276 * 4. filename of zipfile being optimized (used for debug messages and
277 * for comparing against BOOTCLASSPATH; does not need to be
278 * accessible or even exist)
279 * 5. dexopt flags
280 *
281 * The BOOTCLASSPATH environment variable is assumed to hold the correct
282 * boot class path. If the filename provided appears in the boot class
283 * path, the path will be truncated just before that entry (so that, if
284 * you were to dexopt "core.jar", your bootclasspath would be empty).
285 *
286 * This does not try to normalize the boot class path name, so the
287 * filename test won't catch you if you get creative.
288 */
fromZip(int argc,char * const argv[])289 static int fromZip(int argc, char* const argv[])
290 {
291 int result = -1;
292 int zipFd, cacheFd;
293 const char* zipName;
294 char* bcpCopy = NULL;
295 const char* dexoptFlags;
296
297 if (argc != 6) {
298 LOGE("Wrong number of args for --zip (found %d)\n", argc);
299 goto bail;
300 }
301
302 /* skip "--zip" */
303 argc--;
304 argv++;
305
306 GET_ARG(zipFd, strtol, "bad zip fd");
307 GET_ARG(cacheFd, strtol, "bad cache fd");
308 zipName = *++argv;
309 --argc;
310 dexoptFlags = *++argv;
311 --argc;
312
313 result = processZipFile(zipFd, cacheFd, zipName, dexoptFlags);
314
315 bail:
316 return result;
317 }
318
319 /*
320 * Parse arguments for a preoptimization run. This is when dalvikvm is run
321 * on a host to optimize dex files for eventual running on a (different)
322 * device. We want:
323 * 0. (name of dexopt command -- ignored)
324 * 1. "--preopt"
325 * 2. zipfile name
326 * 3. output file name
327 * 4. dexopt flags
328 *
329 * The BOOTCLASSPATH environment variable is assumed to hold the correct
330 * boot class path. If the filename provided appears in the boot class
331 * path, the path will be truncated just before that entry (so that, if
332 * you were to dexopt "core.jar", your bootclasspath would be empty).
333 *
334 * This does not try to normalize the boot class path name, so the
335 * filename test won't catch you if you get creative.
336 */
preopt(int argc,char * const argv[])337 static int preopt(int argc, char* const argv[])
338 {
339 int zipFd = -1;
340 int outFd = -1;
341 int result = -1;
342
343 if (argc != 5) {
344 /*
345 * Use stderr here, since this variant is meant to be called on
346 * the host side.
347 */
348 fprintf(stderr, "Wrong number of args for --preopt (found %d)\n",
349 argc);
350 goto bail;
351 }
352
353 const char* zipName = argv[2];
354 const char* outName = argv[3];
355 const char* dexoptFlags = argv[4];
356
357 if (strstr(dexoptFlags, "u=y") == NULL &&
358 strstr(dexoptFlags, "u=n") == NULL)
359 {
360 fprintf(stderr, "Either 'u=y' or 'u=n' must be specified\n");
361 goto bail;
362 }
363
364 zipFd = open(zipName, O_RDONLY);
365 if (zipFd < 0) {
366 perror(argv[0]);
367 goto bail;
368 }
369
370 outFd = open(outName, O_RDWR | O_EXCL | O_CREAT, 0666);
371 if (outFd < 0) {
372 perror(argv[0]);
373 goto bail;
374 }
375
376 result = processZipFile(zipFd, outFd, zipName, dexoptFlags);
377
378 bail:
379 if (zipFd >= 0) {
380 close(zipFd);
381 }
382
383 if (outFd >= 0) {
384 close(outFd);
385 }
386
387 return result;
388 }
389
390 /*
391 * Parse arguments for an "old-style" invocation directly from the VM.
392 *
393 * Here's what we want:
394 * 0. (name of dexopt command -- ignored)
395 * 1. "--dex"
396 * 2. DALVIK_VM_BUILD value, as a sanity check
397 * 3. file descriptor, locked with flock, for DEX file being optimized
398 * 4. DEX offset within file
399 * 5. DEX length
400 * 6. filename of file being optimized (for debug messages only)
401 * 7. modification date of source (goes into dependency section)
402 * 8. CRC of source (goes into dependency section)
403 * 9. flags (optimization level, isBootstrap)
404 * 10. bootclasspath entry #1
405 * 11. bootclasspath entry #2
406 * ...
407 *
408 * dvmOptimizeDexFile() in dalvik/vm/analysis/DexOptimize.c builds the
409 * argument list and calls this executable.
410 *
411 * The bootclasspath entries become the dependencies for this DEX file.
412 *
413 * The open file descriptor MUST NOT be for one of the bootclasspath files.
414 * The parent has the descriptor locked, and we'll try to lock it again as
415 * part of processing the bootclasspath. (We can catch this and return
416 * an error by comparing filenames or by opening the bootclasspath files
417 * and stat()ing them for inode numbers).
418 */
fromDex(int argc,char * const argv[])419 static int fromDex(int argc, char* const argv[])
420 {
421 int result = -1;
422 bool vmStarted = false;
423 char* bootClassPath = NULL;
424 int fd, flags, vmBuildVersion;
425 long offset, length;
426 const char* debugFileName;
427 u4 crc, modWhen;
428 char* endp;
429
430 if (argc < 10) {
431 /* don't have all mandatory args */
432 LOGE("Not enough arguments for --dex (found %d)\n", 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 LOGE("DexOpt: build rev does not match VM: %d vs %d\n",
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 LOGV("Args: fd=%d off=%ld len=%ld name='%s' mod=0x%x crc=0x%x flg=%d (argc=%d)\n",
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 LOGV("DEP: '%s'\n", *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 LOGV(" bootclasspath is '%s'\n", bootClassPath);
492
493 /* start the VM partway */
494 bool onlyOptVerifiedDex = false;
495 DexClassVerifyMode verifyMode;
496 DexOptimizerMode dexOptMode;
497
498 /* ugh -- upgrade these to a bit field if they get any more complex */
499 if ((flags & DEXOPT_VERIFY_ENABLED) != 0) {
500 if ((flags & DEXOPT_VERIFY_ALL) != 0)
501 verifyMode = VERIFY_MODE_ALL;
502 else
503 verifyMode = VERIFY_MODE_REMOTE;
504 } else {
505 verifyMode = VERIFY_MODE_NONE;
506 }
507 if ((flags & DEXOPT_OPT_ENABLED) != 0) {
508 if ((flags & DEXOPT_OPT_ALL) != 0)
509 dexOptMode = OPTIMIZE_MODE_ALL;
510 else
511 dexOptMode = OPTIMIZE_MODE_VERIFIED;
512 } else {
513 dexOptMode = OPTIMIZE_MODE_NONE;
514 }
515
516 if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) != 0) {
517 LOGE("VM init failed\n");
518 goto bail;
519 }
520
521 vmStarted = true;
522
523 /* do the optimization */
524 if (!dvmContinueOptimization(fd, offset, length, debugFileName,
525 modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
526 {
527 LOGE("Optimization failed\n");
528 goto bail;
529 }
530
531 result = 0;
532
533 bail:
534 /*
535 * In theory we should gracefully shut the VM down at this point. In
536 * practice that only matters if we're checking for memory leaks with
537 * valgrind -- simply exiting is much faster.
538 *
539 * As it turns out, the DEX optimizer plays a little fast and loose
540 * with class loading. We load all of the classes from a partially-
541 * formed DEX file, which is unmapped when we're done. If we want to
542 * do clean shutdown here, perhaps for testing with valgrind, we need
543 * to skip the munmap call there.
544 */
545 #if 0
546 if (vmStarted) {
547 LOGI("DexOpt shutting down, result=%d\n", result);
548 dvmShutdown();
549 }
550 #endif
551
552 //dvmLinearAllocDump(NULL);
553
554 #if 0
555 {
556 extern int gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData,
557 gDvm__gcSimpleData;
558 LOGI("GC DATA: totinst=%d, gcinst=%d, gcdata=%d simpled=%d\n",
559 gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData, gDvm__gcSimpleData);
560 }
561 #endif
562
563 free(bootClassPath);
564 LOGV("DexOpt command complete (result=%d)\n", result);
565 return result;
566 }
567
568 /*
569 * Main entry point. Decide where to go.
570 */
main(int argc,char * const argv[])571 int main(int argc, char* const argv[])
572 {
573 set_process_name("dexopt");
574
575 setvbuf(stdout, NULL, _IONBF, 0);
576
577 if (argc > 1) {
578 if (strcmp(argv[1], "--zip") == 0)
579 return fromZip(argc, argv);
580 else if (strcmp(argv[1], "--dex") == 0)
581 return fromDex(argc, argv);
582 else if (strcmp(argv[1], "--preopt") == 0)
583 return preopt(argc, argv);
584 }
585
586 fprintf(stderr,
587 "Usage:\n\n"
588 "Short version: Don't use this.\n\n"
589 "Slightly longer version: This system-internal tool is used to\n"
590 "produce optimized dex files. See the source code for details.\n");
591
592 return 1;
593 }
594