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 two 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 * 30 * There are some fragile aspects around bootclasspath entries, owing 31 * largely to the VM's history of working on whenever it thought it needed 32 * instead of strictly doing what it was told. If optimizing bootclasspath 33 * entries, always do them in the order in which they appear in the path. 34 */ 35 #include "Dalvik.h" 36 #include "libdex/OptInvocation.h" 37 38 #include "utils/Log.h" 39 #include "cutils/process_name.h" 40 41 #include <stdlib.h> 42 #include <stdio.h> 43 #include <string.h> 44 #include <assert.h> 45 46 static const char* kClassesDex = "classes.dex"; 47 48 49 /* 50 * Extract "classes.dex" from zipFd into "cacheFd", leaving a little space 51 * up front for the DEX optimization header. 52 */ 53 static int extractAndProcessZip(int zipFd, int cacheFd, 54 const char* debugFileName, int isBootstrap, const char* bootClassPath, 55 const char* dexoptFlagStr) 56 { 57 ZipArchive zippy; 58 ZipEntry zipEntry; 59 long uncompLen, modWhen, crc32; 60 off_t dexOffset; 61 int err; 62 int result = -1; 63 64 memset(&zippy, 0, sizeof(zippy)); 65 66 /* make sure we're still at the start of an empty file */ 67 if (lseek(cacheFd, 0, SEEK_END) != 0) { 68 LOGE("DexOptZ: new cache file '%s' is not empty\n", debugFileName); 69 goto bail; 70 } 71 72 /* 73 * Write a skeletal DEX optimization header. We want the classes.dex 74 * to come just after it. 75 */ 76 err = dexOptCreateEmptyHeader(cacheFd); 77 if (err != 0) 78 goto bail; 79 80 /* record the file position so we can get back here later */ 81 dexOffset = lseek(cacheFd, 0, SEEK_CUR); 82 if (dexOffset < 0) 83 goto bail; 84 85 /* 86 * Open the zip archive, find the DEX entry. 87 */ 88 if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) { 89 LOGW("DexOptZ: unable to open zip archive '%s'\n", debugFileName); 90 goto bail; 91 } 92 93 zipEntry = dexZipFindEntry(&zippy, kClassesDex); 94 if (zipEntry == NULL) { 95 LOGW("DexOptZ: zip archive '%s' does not include %s\n", 96 debugFileName, kClassesDex); 97 goto bail; 98 } 99 100 /* 101 * Extract some info about the zip entry. 102 */ 103 if (!dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL, 104 &modWhen, &crc32)) 105 { 106 LOGW("DexOptZ: zip archive GetEntryInfo failed on %s\n", debugFileName); 107 goto bail; 108 } 109 110 uncompLen = uncompLen; 111 modWhen = modWhen; 112 crc32 = crc32; 113 114 /* 115 * Extract the DEX data into the cache file at the current offset. 116 */ 117 if (!dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd)) { 118 LOGW("DexOptZ: extraction of %s from %s failed\n", 119 kClassesDex, debugFileName); 120 goto bail; 121 } 122 123 /* 124 * Prep the VM and perform the optimization. 125 */ 126 DexClassVerifyMode verifyMode = VERIFY_MODE_ALL; 127 DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED; 128 int dexoptFlags = 0; /* bit flags, from enum DexoptFlags */ 129 if (dexoptFlagStr[0] != '\0') { 130 const char* opc; 131 const char* val; 132 133 opc = strstr(dexoptFlagStr, "v="); /* verification */ 134 if (opc != NULL) { 135 switch (*(opc+2)) { 136 case 'n': verifyMode = VERIFY_MODE_NONE; break; 137 case 'r': verifyMode = VERIFY_MODE_REMOTE; break; 138 case 'a': verifyMode = VERIFY_MODE_ALL; break; 139 default: break; 140 } 141 } 142 143 opc = strstr(dexoptFlagStr, "o="); /* optimization */ 144 if (opc != NULL) { 145 switch (*(opc+2)) { 146 case 'n': dexOptMode = OPTIMIZE_MODE_NONE; break; 147 case 'v': dexOptMode = OPTIMIZE_MODE_VERIFIED; break; 148 case 'a': dexOptMode = OPTIMIZE_MODE_ALL; break; 149 default: break; 150 } 151 } 152 153 opc = strstr(dexoptFlagStr, "m=y"); /* register map */ 154 if (opc != NULL) { 155 dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS; 156 } 157 } 158 if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, 159 dexoptFlags) != 0) 160 { 161 LOGE("DexOptZ: VM init failed\n"); 162 goto bail; 163 } 164 165 //vmStarted = 1; 166 167 /* do the optimization */ 168 if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName, 169 modWhen, crc32, isBootstrap)) 170 { 171 LOGE("Optimization failed\n"); 172 goto bail; 173 } 174 175 /* we don't shut the VM down -- process is about to exit */ 176 177 result = 0; 178 179 bail: 180 dexZipCloseArchive(&zippy); 181 return result; 182 } 183 184 185 /* advance to the next arg and extract it */ 186 #define GET_ARG(_var, _func, _msg) \ 187 { \ 188 char* endp; \ 189 (_var) = _func(*++argv, &endp, 0); \ 190 if (*endp != '\0') { \ 191 LOGE("%s '%s'", _msg, *argv); \ 192 goto bail; \ 193 } \ 194 --argc; \ 195 } 196 197 /* 198 * Parse arguments. We want: 199 * 0. (name of dexopt command -- ignored) 200 * 1. "--zip" 201 * 2. zip fd (input, read-only) 202 * 3. cache fd (output, read-write, locked with flock) 203 * 4. filename of file being optimized (used for debug messages and 204 * for comparing against BOOTCLASSPATH -- does not need to be 205 * accessible or even exist) 206 * 5. dexopt flags 207 * 208 * The BOOTCLASSPATH environment variable is assumed to hold the correct 209 * boot class path. If the filename provided appears in the boot class 210 * path, the path will be truncated just before that entry (so that, if 211 * you were to dexopt "core.jar", your bootclasspath would be empty). 212 * 213 * This does not try to normalize the boot class path name, so the 214 * filename test won't catch you if you get creative. 215 */ 216 static int fromZip(int argc, char* const argv[]) 217 { 218 int result = -1; 219 int zipFd, cacheFd, vmBuildVersion; 220 const char* inputFileName; 221 char* bcpCopy = NULL; 222 const char* dexoptFlagStr; 223 224 if (argc != 6) { 225 LOGE("Wrong number of args for --zip (found %d)\n", argc); 226 goto bail; 227 } 228 229 /* skip "--zip" */ 230 argc--; 231 argv++; 232 233 GET_ARG(zipFd, strtol, "bad zip fd"); 234 GET_ARG(cacheFd, strtol, "bad cache fd"); 235 inputFileName = *++argv; 236 --argc; 237 dexoptFlagStr = *++argv; 238 --argc; 239 240 /* 241 * Check to see if this is a bootstrap class entry. If so, truncate 242 * the path. 243 */ 244 const char* bcp = getenv("BOOTCLASSPATH"); 245 if (bcp == NULL) { 246 LOGE("DexOptZ: BOOTCLASSPATH not set\n"); 247 goto bail; 248 } 249 250 int isBootstrap = false; 251 const char* match = strstr(bcp, inputFileName); 252 if (match != NULL) { 253 /* 254 * TODO: we have a partial string match, but that doesn't mean 255 * we've matched an entire path component. We should make sure 256 * that we're matching on the full inputFileName, and if not we 257 * should re-do the strstr starting at (match+1). 258 * 259 * The scenario would be a bootclasspath with something like 260 * "/system/framework/core.jar" while we're trying to optimize 261 * "/framework/core.jar". Not very likely since all paths are 262 * absolute and end with ".jar", but not impossible. 263 */ 264 int matchOffset = match - bcp; 265 if (matchOffset > 0 && bcp[matchOffset-1] == ':') 266 matchOffset--; 267 LOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d\n", 268 inputFileName, matchOffset); 269 bcpCopy = strdup(bcp); 270 bcpCopy[matchOffset] = '\0'; 271 272 bcp = bcpCopy; 273 LOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'\n", bcp); 274 isBootstrap = true; 275 } 276 277 result = extractAndProcessZip(zipFd, cacheFd, inputFileName, 278 isBootstrap, bcp, dexoptFlagStr); 279 280 bail: 281 free(bcpCopy); 282 return result; 283 } 284 285 /* 286 * Parse arguments for an "old-style" invocation directly from the VM. 287 * 288 * Here's what we want: 289 * 0. (name of dexopt command -- ignored) 290 * 1. "--dex" 291 * 2. DALVIK_VM_BUILD value, as a sanity check 292 * 3. file descriptor, locked with flock, for DEX file being optimized 293 * 4. DEX offset within file 294 * 5. DEX length 295 * 6. filename of file being optimized (for debug messages only) 296 * 7. modification date of source (goes into dependency section) 297 * 8. CRC of source (goes into dependency section) 298 * 9. flags (optimization level, isBootstrap) 299 * 10. bootclasspath entry #1 300 * 11. bootclasspath entry #2 301 * ... 302 * 303 * dvmOptimizeDexFile() in dalvik/vm/analysis/DexOptimize.c builds the 304 * argument list and calls this executable. 305 * 306 * The bootclasspath entries become the dependencies for this DEX file. 307 * 308 * The open file descriptor MUST NOT be for one of the bootclasspath files. 309 * The parent has the descriptor locked, and we'll try to lock it again as 310 * part of processing the bootclasspath. (We can catch this and return 311 * an error by comparing filenames or by opening the bootclasspath files 312 * and stat()ing them for inode numbers). 313 */ 314 static int fromDex(int argc, char* const argv[]) 315 { 316 int result = -1; 317 bool vmStarted = false; 318 char* bootClassPath = NULL; 319 int fd, flags, vmBuildVersion; 320 long offset, length; 321 const char* debugFileName; 322 u4 crc, modWhen; 323 char* endp; 324 325 if (argc < 10) { 326 /* don't have all mandatory args */ 327 LOGE("Not enough arguments for --dex (found %d)\n", argc); 328 goto bail; 329 } 330 331 /* skip "--dex" */ 332 argc--; 333 argv++; 334 335 /* 336 * Extract the args. 337 */ 338 GET_ARG(vmBuildVersion, strtol, "bad vm build"); 339 if (vmBuildVersion != DALVIK_VM_BUILD) { 340 LOGE("DexOpt: build rev does not match VM: %d vs %d\n", 341 vmBuildVersion, DALVIK_VM_BUILD); 342 goto bail; 343 } 344 GET_ARG(fd, strtol, "bad fd"); 345 GET_ARG(offset, strtol, "bad offset"); 346 GET_ARG(length, strtol, "bad length"); 347 debugFileName = *++argv; 348 --argc; 349 GET_ARG(modWhen, strtoul, "bad modWhen"); 350 GET_ARG(crc, strtoul, "bad crc"); 351 GET_ARG(flags, strtol, "bad flags"); 352 353 LOGV("Args: fd=%d off=%ld len=%ld name='%s' mod=0x%x crc=0x%x flg=%d (argc=%d)\n", 354 fd, offset, length, debugFileName, modWhen, crc, flags, argc); 355 assert(argc > 0); 356 357 if (--argc == 0) { 358 bootClassPath = strdup(""); 359 } else { 360 int i, bcpLen; 361 char* const* argp; 362 char* cp; 363 364 bcpLen = 0; 365 for (i = 0, argp = argv; i < argc; i++) { 366 ++argp; 367 LOGV("DEP: '%s'\n", *argp); 368 bcpLen += strlen(*argp) + 1; 369 } 370 371 cp = bootClassPath = (char*) malloc(bcpLen +1); 372 for (i = 0, argp = argv; i < argc; i++) { 373 int strLen; 374 375 ++argp; 376 strLen = strlen(*argp); 377 if (i != 0) 378 *cp++ = ':'; 379 memcpy(cp, *argp, strLen); 380 cp += strLen; 381 } 382 *cp = '\0'; 383 384 assert((int) strlen(bootClassPath) == bcpLen-1); 385 } 386 LOGV(" bootclasspath is '%s'\n", bootClassPath); 387 388 /* start the VM partway */ 389 bool onlyOptVerifiedDex = false; 390 DexClassVerifyMode verifyMode; 391 DexOptimizerMode dexOptMode; 392 int dexoptFlags = 0; 393 394 /* ugh -- upgrade these to a bit field if they get any more complex */ 395 if ((flags & DEXOPT_VERIFY_ENABLED) != 0) { 396 if ((flags & DEXOPT_VERIFY_ALL) != 0) 397 verifyMode = VERIFY_MODE_ALL; 398 else 399 verifyMode = VERIFY_MODE_REMOTE; 400 } else { 401 verifyMode = VERIFY_MODE_NONE; 402 } 403 if ((flags & DEXOPT_OPT_ENABLED) != 0) { 404 if ((flags & DEXOPT_OPT_ALL) != 0) 405 dexOptMode = OPTIMIZE_MODE_ALL; 406 else 407 dexOptMode = OPTIMIZE_MODE_VERIFIED; 408 } else { 409 dexOptMode = OPTIMIZE_MODE_NONE; 410 } 411 if ((flags & DEXOPT_GEN_REGISTER_MAP) != 0) { 412 dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS; 413 } 414 415 if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, 416 dexoptFlags) != 0) 417 { 418 LOGE("VM init failed\n"); 419 goto bail; 420 } 421 422 vmStarted = true; 423 424 /* do the optimization */ 425 if (!dvmContinueOptimization(fd, offset, length, debugFileName, 426 modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0)) 427 { 428 LOGE("Optimization failed\n"); 429 goto bail; 430 } 431 432 result = 0; 433 434 bail: 435 /* 436 * In theory we should gracefully shut the VM down at this point. In 437 * practice that only matters if we're checking for memory leaks with 438 * valgrind -- simply exiting is much faster. 439 * 440 * As it turns out, the DEX optimizer plays a little fast and loose 441 * with class loading. We load all of the classes from a partially- 442 * formed DEX file, which is unmapped when we're done. If we want to 443 * do clean shutdown here, perhaps for testing with valgrind, we need 444 * to skip the munmap call there. 445 */ 446 #if 0 447 if (vmStarted) { 448 LOGI("DexOpt shutting down, result=%d\n", result); 449 dvmShutdown(); 450 } 451 #endif 452 453 //dvmLinearAllocDump(NULL); 454 455 #if 0 456 { 457 extern int gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData, 458 gDvm__gcSimpleData; 459 LOGI("GC DATA: totinst=%d, gcinst=%d, gcdata=%d simpled=%d\n", 460 gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData, gDvm__gcSimpleData); 461 } 462 #endif 463 464 free(bootClassPath); 465 LOGV("DexOpt command complete (result=%d)\n", result); 466 return result; 467 } 468 469 /* 470 * Main entry point. Decide where to go. 471 */ 472 int main(int argc, char* const argv[]) 473 { 474 set_process_name("dexopt"); 475 476 setvbuf(stdout, NULL, _IONBF, 0); 477 478 if (argc > 1) { 479 if (strcmp(argv[1], "--zip") == 0) 480 return fromZip(argc, argv); 481 else if (strcmp(argv[1], "--dex") == 0) 482 return fromDex(argc, argv); 483 } 484 485 fprintf(stderr, "Usage: don't use this\n"); 486 return 1; 487 } 488 489