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