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