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 */
extractAndProcessZip(int zipFd,int cacheFd,const char * debugFileName,int isBootstrap,const char * bootClassPath,const char * dexoptFlagStr)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 */
fromZip(int argc,char * const argv[])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 */
fromDex(int argc,char * const argv[])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("Inconsistent build rev: %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 */
main(int argc,char * const argv[])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