blob: 369d70707eb4299a96618a194b17afe1050eca7b [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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 * Convert the output from "dx" into a locally-optimized DEX file.
19 *
20 * TODO: the format of the optimized header is currently "whatever we
21 * happen to write", since the VM that writes it is by definition the same
22 * as the VM that reads it. Still, it should be better documented and
23 * more rigorously structured.
24 */
25#include "Dalvik.h"
26#include "libdex/InstrUtils.h"
27#include "libdex/OptInvocation.h"
The Android Open Source Project99409882009-03-18 22:20:24 -070028#include "analysis/RegisterMap.h"
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080029
30#include <zlib.h>
31
32#include <stdlib.h>
33#include <unistd.h>
34#include <sys/mman.h>
35#include <sys/stat.h>
36#include <sys/file.h>
37#include <sys/wait.h>
38#include <fcntl.h>
39#include <errno.h>
40
41/*
42 * Virtual/direct calls to "method" are replaced with an execute-inline
43 * instruction with index "idx".
44 */
45typedef struct InlineSub {
46 Method* method;
47 int inlineIdx;
48} InlineSub;
49
50
51/* fwd */
52static int writeDependencies(int fd, u4 modWhen, u4 crc);
53static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,\
The Android Open Source Project99409882009-03-18 22:20:24 -070054 const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080055static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
56 int err);
57
58static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,\
59 u4* pHeaderFlags, DexClassLookup** ppClassLookup);
60static void updateChecksum(u1* addr, int len, DexHeader* pHeader);
61static bool loadAllClasses(DvmDex* pDvmDex);
62static void optimizeLoadedClasses(DexFile* pDexFile);
63static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs);
64static bool optimizeMethod(Method* method, const InlineSub* inlineSubs);
65static void rewriteInstField(Method* method, u2* insns, OpCode newOpc);
66static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc);
Andy McFaddenb0a05412009-11-19 10:23:41 -080067static bool rewriteEmptyDirectInvoke(Method* method, u2* insns);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080068static bool rewriteExecuteInline(Method* method, u2* insns,
69 MethodType methodType, const InlineSub* inlineSubs);
Andy McFaddenb0a05412009-11-19 10:23:41 -080070static bool rewriteExecuteInlineRange(Method* method, u2* insns,
71 MethodType methodType, const InlineSub* inlineSubs);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080072
73
74/*
75 * Return the fd of an open file in the DEX file cache area. If the cache
76 * file doesn't exist or is out of date, this will remove the old entry,
77 * create a new one (writing only the file header), and return with the
78 * "new file" flag set.
79 *
80 * It's possible to execute from an unoptimized DEX file directly,
81 * assuming the byte ordering and structure alignment is correct, but
82 * disadvantageous because some significant optimizations are not possible.
83 * It's not generally possible to do the same from an uncompressed Jar
84 * file entry, because we have to guarantee 32-bit alignment in the
85 * memory-mapped file.
86 *
87 * For a Jar/APK file (a zip archive with "classes.dex" inside), "modWhen"
88 * and "crc32" come from the Zip directory entry. For a stand-alone DEX
89 * file, it's the modification date of the file and the Adler32 from the
90 * DEX header (which immediately follows the magic). If these don't
91 * match what's stored in the opt header, we reject the file immediately.
92 *
93 * On success, the file descriptor will be positioned just past the "opt"
94 * file header, and will be locked with flock. "*pCachedName" will point
95 * to newly-allocated storage.
96 */
97int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
98 u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
99{
100 int fd, cc;
101 struct stat fdStat, fileStat;
102 bool readOnly = false;
103
104 *pNewFile = false;
105
106retry:
107 /*
108 * Try to open the cache file. If we've been asked to,
109 * create it if it doesn't exist.
110 */
111 fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
112 if (fd < 0) {
113 fd = open(cacheFileName, O_RDONLY, 0);
114 if (fd < 0) {
115 if (createIfMissing) {
116 LOGE("Can't open dex cache '%s': %s\n",
117 cacheFileName, strerror(errno));
118 }
119 return fd;
120 }
121 readOnly = true;
122 }
123
124 /*
125 * Grab an exclusive lock on the cache file. If somebody else is
126 * working on it, we'll block here until they complete. Because
127 * we're waiting on an external resource, we go into VMWAIT mode.
128 */
129 int oldStatus;
130 LOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)\n",
131 cacheFileName, fd, isBootstrap);
132 oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
133 cc = flock(fd, LOCK_EX | LOCK_NB);
134 if (cc != 0) {
135 LOGD("DexOpt: sleeping on flock(%s)\n", cacheFileName);
136 cc = flock(fd, LOCK_EX);
137 }
138 dvmChangeStatus(NULL, oldStatus);
139 if (cc != 0) {
140 LOGE("Can't lock dex cache '%s': %d\n", cacheFileName, cc);
141 close(fd);
142 return -1;
143 }
144 LOGV("DexOpt: locked cache file\n");
145
146 /*
147 * Check to see if the fd we opened and locked matches the file in
148 * the filesystem. If they don't, then somebody else unlinked ours
149 * and created a new file, and we need to use that one instead. (If
150 * we caught them between the unlink and the create, we'll get an
151 * ENOENT from the file stat.)
152 */
153 cc = fstat(fd, &fdStat);
154 if (cc != 0) {
155 LOGE("Can't stat open file '%s'\n", cacheFileName);
156 LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
157 goto close_fail;
158 }
159 cc = stat(cacheFileName, &fileStat);
160 if (cc != 0 ||
161 fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
162 {
163 LOGD("DexOpt: our open cache file is stale; sleeping and retrying\n");
164 LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
165 flock(fd, LOCK_UN);
166 close(fd);
167 usleep(250 * 1000); /* if something is hosed, don't peg machine */
168 goto retry;
169 }
170
171 /*
172 * We have the correct file open and locked. If the file size is zero,
173 * then it was just created by us, and we want to fill in some fields
174 * in the "opt" header and set "*pNewFile". Otherwise, we want to
175 * verify that the fields in the header match our expectations, and
176 * reset the file if they don't.
177 */
178 if (fdStat.st_size == 0) {
179 if (readOnly) {
180 LOGW("DexOpt: file has zero length and isn't writable\n");
181 goto close_fail;
182 }
183 cc = dexOptCreateEmptyHeader(fd);
184 if (cc != 0)
185 goto close_fail;
186 *pNewFile = true;
187 LOGV("DexOpt: successfully initialized new cache file\n");
188 } else {
189 bool expectVerify, expectOpt;
190
191 if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
192 expectVerify = false;
193 else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
194 expectVerify = !isBootstrap;
195 else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
196 expectVerify = true;
197
198 if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
199 expectOpt = false;
200 else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
201 expectOpt = expectVerify;
202 else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
203 expectOpt = true;
204
205 LOGV("checking deps, expecting vfy=%d opt=%d\n",
206 expectVerify, expectOpt);
207
208 if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
209 expectVerify, expectOpt))
210 {
211 if (readOnly) {
212 /*
213 * We could unlink and rewrite the file if we own it or
214 * the "sticky" bit isn't set on the directory. However,
215 * we're not able to truncate it, which spoils things. So,
216 * give up now.
217 */
218 if (createIfMissing) {
219 LOGW("Cached DEX '%s' (%s) is stale and not writable\n",
220 fileName, cacheFileName);
221 }
222 goto close_fail;
223 }
224
225 /*
226 * If we truncate the existing file before unlinking it, any
227 * process that has it mapped will fail when it tries to touch
228 * the pages.
229 *
230 * This is very important. The zygote process will have the
231 * boot DEX files (core, framework, etc.) mapped early. If
232 * (say) core.dex gets updated, and somebody launches an app
233 * that uses App.dex, then App.dex gets reoptimized because it's
234 * dependent upon the boot classes. However, dexopt will be
235 * using the *new* core.dex to do the optimizations, while the
236 * app will actually be running against the *old* core.dex
237 * because it starts from zygote.
238 *
239 * Even without zygote, it's still possible for a class loader
240 * to pull in an APK that was optimized against an older set
241 * of DEX files. We must ensure that everything fails when a
242 * boot DEX gets updated, and for general "why aren't my
243 * changes doing anything" purposes its best if we just make
244 * everything crash when a DEX they're using gets updated.
245 */
246 LOGD("Stale deps in cache file; removing and retrying\n");
247 if (ftruncate(fd, 0) != 0) {
248 LOGW("Warning: unable to truncate cache file '%s': %s\n",
249 cacheFileName, strerror(errno));
250 /* keep going */
251 }
252 if (unlink(cacheFileName) != 0) {
253 LOGW("Warning: unable to remove cache file '%s': %d %s\n",
254 cacheFileName, errno, strerror(errno));
255 /* keep going; permission failure should probably be fatal */
256 }
257 LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
258 flock(fd, LOCK_UN);
259 close(fd);
260 goto retry;
261 } else {
262 LOGV("DexOpt: good deps in cache file\n");
263 }
264 }
265
266 assert(fd >= 0);
267 return fd;
268
269close_fail:
270 flock(fd, LOCK_UN);
271 close(fd);
272 return -1;
273}
274
275/*
276 * Unlock the file descriptor.
277 *
278 * Returns "true" on success.
279 */
280bool dvmUnlockCachedDexFile(int fd)
281{
282 LOGVV("DexOpt: unlocking cache file fd=%d\n", fd);
283 return (flock(fd, LOCK_UN) == 0);
284}
285
286
287/*
288 * Given a descriptor for a file with DEX data in it, produce an
289 * optimized version.
290 *
291 * The file pointed to by "fd" is expected to be a locked shared resource
292 * (or private); we make no efforts to enforce multi-process correctness
293 * here.
294 *
295 * "fileName" is only used for debug output. "modWhen" and "crc" are stored
296 * in the dependency set.
297 *
298 * The "isBootstrap" flag determines how the optimizer and verifier handle
299 * package-scope access checks. When optimizing, we only load the bootstrap
300 * class DEX files and the target DEX, so the flag determines whether the
301 * target DEX classes are given a (synthetic) non-NULL classLoader pointer.
302 * This only really matters if the target DEX contains classes that claim to
303 * be in the same package as bootstrap classes.
304 *
305 * The optimizer will need to load every class in the target DEX file.
306 * This is generally undesirable, so we start a subprocess to do the
307 * work and wait for it to complete.
308 *
309 * Returns "true" on success. All data will have been written to "fd".
310 */
311bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
312 const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
313{
314 const char* lastPart = strrchr(fileName, '/');
315 if (lastPart != NULL)
316 lastPart++;
317 else
318 lastPart = fileName;
319
320 /*
321 * For basic optimizations (byte-swapping and structure aligning) we
322 * don't need to fork(). It looks like fork+exec is causing problems
323 * with gdb on our bewildered Linux distro, so in some situations we
324 * want to avoid this.
325 *
326 * For optimization and/or verification, we need to load all the classes.
327 *
328 * We don't check gDvm.generateRegisterMaps, since that is dependent
329 * upon the verifier state.
330 */
331 if (gDvm.classVerifyMode == VERIFY_MODE_NONE &&
332 (gDvm.dexOptMode == OPTIMIZE_MODE_NONE ||
333 gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED))
334 {
335 LOGD("DexOpt: --- BEGIN (quick) '%s' ---\n", lastPart);
336 return dvmContinueOptimization(fd, dexOffset, dexLength,
337 fileName, modWhen, crc, isBootstrap);
338 }
339
340
341 LOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---\n", lastPart, isBootstrap);
342
343 pid_t pid;
344
345 /*
346 * This could happen if something in our bootclasspath, which we thought
347 * was all optimized, got rejected.
348 */
349 if (gDvm.optimizing) {
350 LOGW("Rejecting recursive optimization attempt on '%s'\n", fileName);
351 return false;
352 }
353
354 pid = fork();
355 if (pid == 0) {
356 static const int kUseValgrind = 0;
357 static const char* kDexOptBin = "/bin/dexopt";
358 static const char* kValgrinder = "/usr/bin/valgrind";
359 static const int kFixedArgCount = 10;
360 static const int kValgrindArgCount = 5;
361 static const int kMaxIntLen = 12; // '-'+10dig+'\0' -OR- 0x+8dig
362 int bcpSize = dvmGetBootPathSize();
363 int argc = kFixedArgCount + bcpSize
364 + (kValgrindArgCount * kUseValgrind);
365 char* argv[argc+1]; // last entry is NULL
366 char values[argc][kMaxIntLen];
367 char* execFile;
368 char* androidRoot;
369 int flags;
370
Andy McFadden074afd62009-04-08 12:51:10 -0700371 /* change process groups, so we don't clash with ProcessManager */
372 setpgid(0, 0);
373
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800374 /* full path to optimizer */
375 androidRoot = getenv("ANDROID_ROOT");
376 if (androidRoot == NULL) {
377 LOGW("ANDROID_ROOT not set, defaulting to /system\n");
378 androidRoot = "/system";
379 }
380 execFile = malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
381 strcpy(execFile, androidRoot);
382 strcat(execFile, kDexOptBin);
383
384 /*
385 * Create arg vector.
386 */
387 int curArg = 0;
388
389 if (kUseValgrind) {
390 /* probably shouldn't ship the hard-coded path */
391 argv[curArg++] = (char*)kValgrinder;
392 argv[curArg++] = "--tool=memcheck";
393 argv[curArg++] = "--leak-check=yes"; // check for leaks too
394 argv[curArg++] = "--leak-resolution=med"; // increase from 2 to 4
395 argv[curArg++] = "--num-callers=16"; // default is 12
396 assert(curArg == kValgrindArgCount);
397 }
398 argv[curArg++] = execFile;
399
400 argv[curArg++] = "--dex";
401
402 sprintf(values[2], "%d", DALVIK_VM_BUILD);
403 argv[curArg++] = values[2];
404
405 sprintf(values[3], "%d", fd);
406 argv[curArg++] = values[3];
407
408 sprintf(values[4], "%d", (int) dexOffset);
409 argv[curArg++] = values[4];
410
411 sprintf(values[5], "%d", (int) dexLength);
412 argv[curArg++] = values[5];
413
414 argv[curArg++] = (char*)fileName;
415
416 sprintf(values[7], "%d", (int) modWhen);
417 argv[curArg++] = values[7];
418
419 sprintf(values[8], "%d", (int) crc);
420 argv[curArg++] = values[8];
421
422 flags = 0;
423 if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE) {
424 flags |= DEXOPT_OPT_ENABLED;
425 if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
426 flags |= DEXOPT_OPT_ALL;
427 }
428 if (gDvm.classVerifyMode != VERIFY_MODE_NONE) {
429 flags |= DEXOPT_VERIFY_ENABLED;
430 if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
431 flags |= DEXOPT_VERIFY_ALL;
432 }
433 if (isBootstrap)
434 flags |= DEXOPT_IS_BOOTSTRAP;
435 if (gDvm.generateRegisterMaps)
436 flags |= DEXOPT_GEN_REGISTER_MAP;
437 sprintf(values[9], "%d", flags);
438 argv[curArg++] = values[9];
439
440 assert(((!kUseValgrind && curArg == kFixedArgCount) ||
441 ((kUseValgrind && curArg == kFixedArgCount+kValgrindArgCount))));
442
443 ClassPathEntry* cpe;
444 for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
445 argv[curArg++] = cpe->fileName;
446 }
447 assert(curArg == argc);
448
449 argv[curArg] = NULL;
450
451 if (kUseValgrind)
452 execv(kValgrinder, argv);
453 else
454 execv(execFile, argv);
455
456 LOGE("execv '%s'%s failed: %s\n", execFile,
457 kUseValgrind ? " [valgrind]" : "", strerror(errno));
458 exit(1);
459 } else {
460 LOGV("DexOpt: waiting for verify+opt, pid=%d\n", (int) pid);
461 int status;
462 pid_t gotPid;
463 int oldStatus;
464
465 /*
466 * Wait for the optimization process to finish. We go into VMWAIT
467 * mode here so GC suspension won't have to wait for us.
468 */
469 oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
470 while (true) {
471 gotPid = waitpid(pid, &status, 0);
472 if (gotPid == -1 && errno == EINTR) {
473 LOGD("waitpid interrupted, retrying\n");
474 } else {
475 break;
476 }
477 }
478 dvmChangeStatus(NULL, oldStatus);
479 if (gotPid != pid) {
480 LOGE("waitpid failed: wanted %d, got %d: %s\n",
481 (int) pid, (int) gotPid, strerror(errno));
482 return false;
483 }
484
485 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
486 LOGD("DexOpt: --- END '%s' (success) ---\n", lastPart);
487 return true;
488 } else {
489 LOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed\n",
490 lastPart, status);
491 return false;
492 }
493 }
494}
495
496/*
497 * Do the actual optimization. This is called directly for "minimal"
498 * optimization, or from a newly-created process for "full" optimization.
499 *
500 * For best use of disk/memory, we want to extract once and perform
501 * optimizations in place. If the file has to expand or contract
502 * to match local structure padding/alignment expectations, we want
503 * to do the rewrite as part of the extract, rather than extracting
504 * into a temp file and slurping it back out. (The structure alignment
505 * is currently correct for all platforms, and this isn't expected to
506 * change, so we should be okay with having it already extracted.)
507 *
508 * Returns "true" on success.
509 */
510bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
511 const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
512{
513 DexClassLookup* pClassLookup = NULL;
514 IndexMapSet* pIndexMapSet = NULL;
The Android Open Source Project99409882009-03-18 22:20:24 -0700515 RegisterMapBuilder* pRegMapBuilder = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800516 bool doVerify, doOpt;
517 u4 headerFlags = 0;
518
519 if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
520 doVerify = false;
521 else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
522 doVerify = !isBootstrap;
523 else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
524 doVerify = true;
525
526 if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
527 doOpt = false;
528 else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
529 doOpt = doVerify;
530 else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
531 doOpt = true;
532
533 LOGV("Continuing optimization (%s, isb=%d, vfy=%d, opt=%d)\n",
534 fileName, isBootstrap, doVerify, doOpt);
535
536 assert(dexOffset >= 0);
537
538 /* quick test so we don't blow up on empty file */
539 if (dexLength < (int) sizeof(DexHeader)) {
540 LOGE("too small to be DEX\n");
541 return false;
542 }
543 if (dexOffset < (int) sizeof(DexOptHeader)) {
544 LOGE("not enough room for opt header\n");
545 return false;
546 }
547
548 bool result = false;
549
550 /*
551 * Drop this into a global so we don't have to pass it around. We could
552 * also add a field to DexFile, but since it only pertains to DEX
553 * creation that probably doesn't make sense.
554 */
555 gDvm.optimizingBootstrapClass = isBootstrap;
556
557 {
558 /*
559 * Map the entire file (so we don't have to worry about page
560 * alignment). The expectation is that the output file contains
561 * our DEX data plus room for a small header.
562 */
563 bool success;
564 void* mapAddr;
565 mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,
566 MAP_SHARED, fd, 0);
567 if (mapAddr == MAP_FAILED) {
568 LOGE("unable to mmap DEX cache: %s\n", strerror(errno));
569 goto bail;
570 }
571
572 /*
573 * Rewrite the file. Byte reordering, structure realigning,
574 * class verification, and bytecode optimization are all performed
575 * here.
The Android Open Source Project99409882009-03-18 22:20:24 -0700576 *
577 * In theory the file could change size and bits could shift around.
578 * In practice this would be annoying to deal with, so the file
579 * layout is designed so that it can always be rewritten in place.
580 *
581 * This sets "headerFlags" and creates the class lookup table as
582 * part of doing the processing.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800583 */
584 success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
585 doVerify, doOpt, &headerFlags, &pClassLookup);
586
587 if (success) {
588 DvmDex* pDvmDex = NULL;
589 u1* dexAddr = ((u1*) mapAddr) + dexOffset;
590
591 if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
592 LOGE("Unable to create DexFile\n");
The Android Open Source Project99409882009-03-18 22:20:24 -0700593 success = false;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800594 } else {
595 /*
596 * If configured to do so, scan the instructions, looking
597 * for ways to reduce the size of the resolved-constant table.
598 * This is done post-optimization, across the instructions
599 * in all methods in all classes (even the ones that failed
600 * to load).
601 */
602 pIndexMapSet = dvmRewriteConstants(pDvmDex);
603
The Android Open Source Project99409882009-03-18 22:20:24 -0700604 /*
605 * If configured to do so, generate a full set of register
606 * maps for all verified classes.
607 */
608 if (gDvm.generateRegisterMaps) {
609 pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);
610 if (pRegMapBuilder == NULL) {
611 LOGE("Failed generating register maps\n");
612 success = false;
613 }
614 }
615
Andy McFaddenb51ea112009-05-08 16:50:17 -0700616 DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;
617 updateChecksum(dexAddr, dexLength, pHeader);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800618
619 dvmDexFileFree(pDvmDex);
620 }
621 }
622
623 /* unmap the read-write version, forcing writes to disk */
624 if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) {
625 LOGW("msync failed: %s\n", strerror(errno));
626 // weird, but keep going
627 }
628#if 1
629 /*
630 * This causes clean shutdown to fail, because we have loaded classes
631 * that point into it. For the optimizer this isn't a problem,
632 * because it's more efficient for the process to simply exit.
633 * Exclude this code when doing clean shutdown for valgrind.
634 */
635 if (munmap(mapAddr, dexOffset + dexLength) != 0) {
636 LOGE("munmap failed: %s\n", strerror(errno));
637 goto bail;
638 }
639#endif
640
641 if (!success)
642 goto bail;
643 }
644
645 /* get start offset, and adjust deps start for 64-bit alignment */
646 off_t depsOffset, auxOffset, endOffset, adjOffset;
647 int depsLength, auxLength;
648
649 depsOffset = lseek(fd, 0, SEEK_END);
650 if (depsOffset < 0) {
651 LOGE("lseek to EOF failed: %s\n", strerror(errno));
652 goto bail;
653 }
654 adjOffset = (depsOffset + 7) & ~(0x07);
655 if (adjOffset != depsOffset) {
656 LOGV("Adjusting deps start from %d to %d\n",
657 (int) depsOffset, (int) adjOffset);
658 depsOffset = adjOffset;
659 lseek(fd, depsOffset, SEEK_SET);
660 }
661
662 /*
663 * Append the dependency list.
664 */
665 if (writeDependencies(fd, modWhen, crc) != 0) {
666 LOGW("Failed writing dependencies\n");
667 goto bail;
668 }
669
The Android Open Source Project99409882009-03-18 22:20:24 -0700670 /* compute deps length, then adjust aux start for 64-bit alignment */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800671 auxOffset = lseek(fd, 0, SEEK_END);
672 depsLength = auxOffset - depsOffset;
673
674 adjOffset = (auxOffset + 7) & ~(0x07);
675 if (adjOffset != auxOffset) {
676 LOGV("Adjusting aux start from %d to %d\n",
677 (int) auxOffset, (int) adjOffset);
678 auxOffset = adjOffset;
679 lseek(fd, auxOffset, SEEK_SET);
680 }
681
682 /*
683 * Append any auxillary pre-computed data structures.
684 */
The Android Open Source Project99409882009-03-18 22:20:24 -0700685 if (!writeAuxData(fd, pClassLookup, pIndexMapSet, pRegMapBuilder)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800686 LOGW("Failed writing aux data\n");
687 goto bail;
688 }
689
690 endOffset = lseek(fd, 0, SEEK_END);
691 auxLength = endOffset - auxOffset;
692
693 /*
694 * Output the "opt" header with all values filled in and a correct
695 * magic number.
696 */
697 DexOptHeader optHdr;
698 memset(&optHdr, 0xff, sizeof(optHdr));
699 memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);
700 memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);
701 optHdr.dexOffset = (u4) dexOffset;
702 optHdr.dexLength = (u4) dexLength;
703 optHdr.depsOffset = (u4) depsOffset;
704 optHdr.depsLength = (u4) depsLength;
705 optHdr.auxOffset = (u4) auxOffset;
706 optHdr.auxLength = (u4) auxLength;
707
708 optHdr.flags = headerFlags;
709
710 ssize_t actual;
711 lseek(fd, 0, SEEK_SET);
712 actual = write(fd, &optHdr, sizeof(optHdr));
713 if (actual != sizeof(optHdr)) {
714 logFailedWrite(sizeof(optHdr), actual, "opt header", errno);
715 goto bail;
716 }
717
718 LOGV("Successfully wrote DEX header\n");
719 result = true;
720
The Android Open Source Project99409882009-03-18 22:20:24 -0700721 //dvmRegisterMapDumpStats();
722
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800723bail:
724 dvmFreeIndexMapSet(pIndexMapSet);
The Android Open Source Project99409882009-03-18 22:20:24 -0700725 dvmFreeRegisterMapBuilder(pRegMapBuilder);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800726 free(pClassLookup);
727 return result;
728}
729
730
731/*
732 * Get the cache file name from a ClassPathEntry.
733 */
734static const char* getCacheFileName(const ClassPathEntry* cpe)
735{
736 switch (cpe->kind) {
737 case kCpeJar:
738 return dvmGetJarFileCacheFileName((JarFile*) cpe->ptr);
739 case kCpeDex:
740 return dvmGetRawDexFileCacheFileName((RawDexFile*) cpe->ptr);
741 default:
742 LOGE("DexOpt: unexpected cpe kind %d\n", cpe->kind);
743 dvmAbort();
744 return NULL;
745 }
746}
747
748/*
749 * Get the SHA-1 signature.
750 */
751static const u1* getSignature(const ClassPathEntry* cpe)
752{
753 DvmDex* pDvmDex;
754
755 switch (cpe->kind) {
756 case kCpeJar:
757 pDvmDex = dvmGetJarFileDex((JarFile*) cpe->ptr);
758 break;
759 case kCpeDex:
760 pDvmDex = dvmGetRawDexFileDex((RawDexFile*) cpe->ptr);
761 break;
762 default:
763 LOGE("unexpected cpe kind %d\n", cpe->kind);
764 dvmAbort();
765 pDvmDex = NULL; // make gcc happy
766 }
767
768 assert(pDvmDex != NULL);
769 return pDvmDex->pDexFile->pHeader->signature;
770}
771
772
773/*
774 * Dependency layout:
775 * 4b Source file modification time, in seconds since 1970 UTC
776 * 4b CRC-32 from Zip entry, or Adler32 from source DEX header
777 * 4b Dalvik VM build number
778 * 4b Number of dependency entries that follow
779 * Dependency entries:
780 * 4b Name length (including terminating null)
781 * var Full path of cache entry (null terminated)
782 * 20b SHA-1 signature from source DEX file
783 *
784 * If this changes, update DEX_OPT_MAGIC_VERS.
785 */
786static const size_t kMinDepSize = 4 * 4;
787static const size_t kMaxDepSize = 4 * 4 + 1024; // sanity check
788
789/*
790 * Read the "opt" header, verify it, then read the dependencies section
791 * and verify that data as well.
792 *
793 * If "sourceAvail" is "true", this will verify that "modWhen" and "crc"
794 * match up with what is stored in the header. If they don't, we reject
795 * the file so that it can be recreated from the updated original. If
796 * "sourceAvail" isn't set, e.g. for a .odex file, we ignore these arguments.
797 *
798 * On successful return, the file will be seeked immediately past the
799 * "opt" header.
800 */
801bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
802 u4 crc, bool expectVerify, bool expectOpt)
803{
804 DexOptHeader optHdr;
805 u1* depData = NULL;
806 const u1* magic;
807 off_t posn;
808 int result = false;
809 ssize_t actual;
810
811 /*
812 * Start at the start. The "opt" header, when present, will always be
813 * the first thing in the file.
814 */
815 if (lseek(fd, 0, SEEK_SET) != 0) {
816 LOGE("DexOpt: failed to seek to start of file: %s\n", strerror(errno));
817 goto bail;
818 }
819
820 /*
821 * Read and do trivial verification on the opt header. The header is
822 * always in host byte order.
823 */
824 if (read(fd, &optHdr, sizeof(optHdr)) != sizeof(optHdr)) {
825 LOGE("DexOpt: failed reading opt header: %s\n", strerror(errno));
826 goto bail;
827 }
828
829 magic = optHdr.magic;
830 if (memcmp(magic, DEX_OPT_MAGIC, 4) != 0) {
831 /* not a DEX file, or previous attempt was interrupted */
832 LOGD("DexOpt: incorrect opt magic number (0x%02x %02x %02x %02x)\n",
833 magic[0], magic[1], magic[2], magic[3]);
834 goto bail;
835 }
836 if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
837 LOGW("DexOpt: stale opt version (0x%02x %02x %02x %02x)\n",
838 magic[4], magic[5], magic[6], magic[7]);
839 goto bail;
840 }
841 if (optHdr.depsLength < kMinDepSize || optHdr.depsLength > kMaxDepSize) {
842 LOGW("DexOpt: weird deps length %d, bailing\n", optHdr.depsLength);
843 goto bail;
844 }
845
846 /*
847 * Do the header flags match up with what we want?
848 *
849 * This is useful because it allows us to automatically regenerate
850 * a file when settings change (e.g. verification is now mandatory),
851 * but can cause difficulties if the bootstrap classes we depend upon
852 * were handled differently than the current options specify. We get
853 * upset because they're not verified or optimized, but we're not able
854 * to regenerate them because the installer won't let us.
855 *
856 * (This is also of limited value when !sourceAvail.)
857 *
858 * So, for now, we essentially ignore "expectVerify" and "expectOpt"
859 * by limiting the match mask.
860 *
861 * The only thing we really can't handle is incorrect byte-ordering.
862 */
863 const u4 matchMask = DEX_OPT_FLAG_BIG;
864 u4 expectedFlags = 0;
865#if __BYTE_ORDER != __LITTLE_ENDIAN
866 expectedFlags |= DEX_OPT_FLAG_BIG;
867#endif
868 if (expectVerify)
869 expectedFlags |= DEX_FLAG_VERIFIED;
870 if (expectOpt)
871 expectedFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
872 if ((expectedFlags & matchMask) != (optHdr.flags & matchMask)) {
873 LOGI("DexOpt: header flag mismatch (0x%02x vs 0x%02x, mask=0x%02x)\n",
874 expectedFlags, optHdr.flags, matchMask);
875 goto bail;
876 }
877
878 posn = lseek(fd, optHdr.depsOffset, SEEK_SET);
879 if (posn < 0) {
880 LOGW("DexOpt: seek to deps failed: %s\n", strerror(errno));
881 goto bail;
882 }
883
884 /*
885 * Read all of the dependency stuff into memory.
886 */
887 depData = (u1*) malloc(optHdr.depsLength);
888 if (depData == NULL) {
889 LOGW("DexOpt: unable to allocate %d bytes for deps\n",
890 optHdr.depsLength);
891 goto bail;
892 }
893 actual = read(fd, depData, optHdr.depsLength);
894 if (actual != (ssize_t) optHdr.depsLength) {
895 LOGW("DexOpt: failed reading deps: %d of %d (err=%s)\n",
896 (int) actual, optHdr.depsLength, strerror(errno));
897 goto bail;
898 }
899
900 /*
901 * Verify simple items.
902 */
903 const u1* ptr;
904 u4 val;
905
906 ptr = depData;
907 val = read4LE(&ptr);
908 if (sourceAvail && val != modWhen) {
909 LOGI("DexOpt: source file mod time mismatch (%08x vs %08x)\n",
910 val, modWhen);
911 goto bail;
912 }
913 val = read4LE(&ptr);
914 if (sourceAvail && val != crc) {
915 LOGI("DexOpt: source file CRC mismatch (%08x vs %08x)\n", val, crc);
916 goto bail;
917 }
918 val = read4LE(&ptr);
919 if (val != DALVIK_VM_BUILD) {
Andy McFadden23e84b12009-06-18 14:22:22 -0700920 LOGD("DexOpt: VM build version mismatch (%d vs %d)\n",
921 val, DALVIK_VM_BUILD);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800922 goto bail;
923 }
924
925 /*
926 * Verify dependencies on other cached DEX files. It must match
927 * exactly with what is currently defined in the bootclasspath.
928 */
929 ClassPathEntry* cpe;
930 u4 numDeps;
931
932 numDeps = read4LE(&ptr);
933 LOGV("+++ DexOpt: numDeps = %d\n", numDeps);
934 for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
935 const char* cacheFileName = getCacheFileName(cpe);
936 const u1* signature = getSignature(cpe);
937 size_t len = strlen(cacheFileName) +1;
938 u4 storedStrLen;
939
940 if (numDeps == 0) {
941 /* more entries in bootclasspath than in deps list */
942 LOGI("DexOpt: not all deps represented\n");
943 goto bail;
944 }
945
946 storedStrLen = read4LE(&ptr);
947 if (len != storedStrLen ||
948 strcmp(cacheFileName, (const char*) ptr) != 0)
949 {
950 LOGI("DexOpt: mismatch dep name: '%s' vs. '%s'\n",
951 cacheFileName, ptr);
952 goto bail;
953 }
954
955 ptr += storedStrLen;
956
957 if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
958 LOGI("DexOpt: mismatch dep signature for '%s'\n", cacheFileName);
959 goto bail;
960 }
961 ptr += kSHA1DigestLen;
962
963 LOGV("DexOpt: dep match on '%s'\n", cacheFileName);
964
965 numDeps--;
966 }
967
968 if (numDeps != 0) {
969 /* more entries in deps list than in classpath */
970 LOGI("DexOpt: Some deps went away\n");
971 goto bail;
972 }
973
974 // consumed all data and no more?
975 if (ptr != depData + optHdr.depsLength) {
976 LOGW("DexOpt: Spurious dep data? %d vs %d\n",
977 (int) (ptr - depData), optHdr.depsLength);
978 assert(false);
979 }
980
981 result = true;
982
983bail:
984 free(depData);
985 return result;
986}
987
988/*
989 * Write the dependency info to "fd" at the current file position.
990 */
991static int writeDependencies(int fd, u4 modWhen, u4 crc)
992{
993 u1* buf = NULL;
994 ssize_t actual;
995 int result = -1;
996 ssize_t bufLen;
997 ClassPathEntry* cpe;
998 int i, numDeps;
999
1000 /*
1001 * Count up the number of completed entries in the bootclasspath.
1002 */
1003 numDeps = 0;
1004 bufLen = 0;
1005 for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
1006 const char* cacheFileName = getCacheFileName(cpe);
1007 LOGV("+++ DexOpt: found dep '%s'\n", cacheFileName);
1008
1009 numDeps++;
1010 bufLen += strlen(cacheFileName) +1;
1011 }
1012
1013 bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
1014
1015 buf = malloc(bufLen);
1016
1017 set4LE(buf+0, modWhen);
1018 set4LE(buf+4, crc);
1019 set4LE(buf+8, DALVIK_VM_BUILD);
1020 set4LE(buf+12, numDeps);
1021
1022 // TODO: do we want to add dvmGetInlineOpsTableLength() here? Won't
1023 // help us if somebody replaces an existing entry, but it'd catch
1024 // additions/removals.
1025
1026 u1* ptr = buf + 4*4;
1027 for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
1028 const char* cacheFileName = getCacheFileName(cpe);
1029 const u1* signature = getSignature(cpe);
1030 int len = strlen(cacheFileName) +1;
1031
1032 if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
1033 LOGE("DexOpt: overran buffer\n");
1034 dvmAbort();
1035 }
1036
1037 set4LE(ptr, len);
1038 ptr += 4;
1039 memcpy(ptr, cacheFileName, len);
1040 ptr += len;
1041 memcpy(ptr, signature, kSHA1DigestLen);
1042 ptr += kSHA1DigestLen;
1043 }
1044
1045 assert(ptr == buf + bufLen);
1046
1047 actual = write(fd, buf, bufLen);
1048 if (actual != bufLen) {
1049 result = (errno != 0) ? errno : -1;
1050 logFailedWrite(bufLen, actual, "dep info", errno);
1051 } else {
1052 result = 0;
1053 }
1054
1055 free(buf);
1056 return result;
1057}
1058
1059
1060/*
1061 * Write a block of data in "chunk" format.
1062 *
1063 * The chunk header fields are always in "native" byte order. If "size"
1064 * is not a multiple of 8 bytes, the data area is padded out.
1065 */
1066static bool writeChunk(int fd, u4 type, const void* data, size_t size)
1067{
1068 ssize_t actual;
1069 union { /* save a syscall by grouping these together */
1070 char raw[8];
1071 struct {
1072 u4 type;
1073 u4 size;
1074 } ts;
1075 } header;
1076
1077 assert(sizeof(header) == 8);
1078
1079 LOGV("Writing chunk, type=%.4s size=%d\n", (char*) &type, size);
1080
1081 header.ts.type = type;
1082 header.ts.size = (u4) size;
1083 actual = write(fd, &header, sizeof(header));
1084 if (actual != sizeof(header)) {
1085 logFailedWrite(size, actual, "aux chunk header write", errno);
1086 return false;
1087 }
1088
1089 if (size > 0) {
1090 actual = write(fd, data, size);
1091 if (actual != (ssize_t) size) {
1092 logFailedWrite(size, actual, "aux chunk write", errno);
1093 return false;
1094 }
1095 }
1096
1097 /* if necessary, pad to 64-bit alignment */
1098 if ((size & 7) != 0) {
1099 int padSize = 8 - (size & 7);
1100 LOGV("size was %d, inserting %d pad bytes\n", size, padSize);
1101 lseek(fd, padSize, SEEK_CUR);
1102 }
1103
1104 assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
1105
1106 return true;
1107}
1108
1109/*
1110 * Write aux data.
1111 *
1112 * We have different pieces, some of which may be optional. To make the
1113 * most effective use of space, we use a "chunk" format, with a 4-byte
1114 * type and a 4-byte length. We guarantee 64-bit alignment for the data,
1115 * so it can be used directly when the file is mapped for reading.
1116 */
1117static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,
The Android Open Source Project99409882009-03-18 22:20:24 -07001118 const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001119{
1120 /* pre-computed class lookup hash table */
The Android Open Source Project99409882009-03-18 22:20:24 -07001121 if (!writeChunk(fd, (u4) kDexChunkClassLookup,
1122 pClassLookup, pClassLookup->size))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001123 {
1124 return false;
1125 }
1126
1127 /* remapped constants (optional) */
1128 if (pIndexMapSet != NULL) {
The Android Open Source Project99409882009-03-18 22:20:24 -07001129 if (!writeChunk(fd, pIndexMapSet->chunkType,
1130 pIndexMapSet->chunkData, pIndexMapSet->chunkDataLen))
1131 {
1132 return false;
1133 }
1134 }
1135
1136 /* register maps (optional) */
1137 if (pRegMapBuilder != NULL) {
1138 if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
1139 pRegMapBuilder->data, pRegMapBuilder->size))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001140 {
1141 return false;
1142 }
1143 }
1144
1145 /* write the end marker */
1146 if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
1147 return false;
1148 }
1149
1150 return true;
1151}
1152
1153/*
1154 * Log a failed write.
1155 */
1156static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
1157 int err)
1158{
1159 LOGE("Write failed: %s (%d of %d): %s\n",
1160 msg, (int)actual, (int)expected, strerror(err));
1161}
1162
1163
1164/*
1165 * ===========================================================================
1166 * Optimizations
1167 * ===========================================================================
1168 */
1169
1170/*
1171 * Perform in-place rewrites on a memory-mapped DEX file.
1172 *
1173 * This happens in a short-lived child process, so we can go nutty with
1174 * loading classes and allocating memory.
1175 */
1176static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
1177 u4* pHeaderFlags, DexClassLookup** ppClassLookup)
1178{
1179 u8 prepWhen, loadWhen, verifyWhen, optWhen;
1180 DvmDex* pDvmDex = NULL;
1181 bool result = false;
1182
1183 *pHeaderFlags = 0;
1184
1185 LOGV("+++ swapping bytes\n");
1186 if (dexFixByteOrdering(addr, len) != 0)
1187 goto bail;
1188#if __BYTE_ORDER != __LITTLE_ENDIAN
1189 *pHeaderFlags |= DEX_OPT_FLAG_BIG;
1190#endif
1191
1192 /*
1193 * Now that the DEX file can be read directly, create a DexFile for it.
1194 */
1195 if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
1196 LOGE("Unable to create DexFile\n");
1197 goto bail;
1198 }
1199
1200 /*
1201 * Create the class lookup table.
1202 */
1203 //startWhen = dvmGetRelativeTimeUsec();
1204 *ppClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
1205 if (*ppClassLookup == NULL)
1206 goto bail;
1207
1208 /*
1209 * Bail out early if they don't want The Works. The current implementation
1210 * doesn't fork a new process if this flag isn't set, so we really don't
1211 * want to continue on with the crazy class loading.
1212 */
1213 if (!doVerify && !doOpt) {
1214 result = true;
1215 goto bail;
1216 }
1217
1218 /* this is needed for the next part */
1219 pDvmDex->pDexFile->pClassLookup = *ppClassLookup;
1220
1221 prepWhen = dvmGetRelativeTimeUsec();
1222
1223 /*
1224 * Load all classes found in this DEX file. If they fail to load for
1225 * some reason, they won't get verified (which is as it should be).
1226 */
1227 if (!loadAllClasses(pDvmDex))
1228 goto bail;
1229 loadWhen = dvmGetRelativeTimeUsec();
1230
1231 /*
1232 * Verify all classes in the DEX file. Export the "is verified" flag
1233 * to the DEX file we're creating.
1234 */
1235 if (doVerify) {
1236 dvmVerifyAllClasses(pDvmDex->pDexFile);
1237 *pHeaderFlags |= DEX_FLAG_VERIFIED;
1238 }
1239 verifyWhen = dvmGetRelativeTimeUsec();
1240
1241 /*
1242 * Optimize the classes we successfully loaded. If the opt mode is
1243 * OPTIMIZE_MODE_VERIFIED, each class must have been successfully
1244 * verified or we'll skip it.
1245 */
1246#ifndef PROFILE_FIELD_ACCESS
1247 if (doOpt) {
1248 optimizeLoadedClasses(pDvmDex->pDexFile);
1249 *pHeaderFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
1250 }
1251#endif
1252 optWhen = dvmGetRelativeTimeUsec();
1253
1254 LOGD("DexOpt: load %dms, verify %dms, opt %dms\n",
1255 (int) (loadWhen - prepWhen) / 1000,
1256 (int) (verifyWhen - loadWhen) / 1000,
1257 (int) (optWhen - verifyWhen) / 1000);
1258
1259 result = true;
1260
1261bail:
1262 /* free up storage */
1263 dvmDexFileFree(pDvmDex);
1264
1265 return result;
1266}
1267
1268/*
1269 * Update the Adler-32 checksum stored in the DEX file. This covers the
1270 * swapped and optimized DEX data, but does not include the opt header
1271 * or auxillary data.
1272 */
1273static void updateChecksum(u1* addr, int len, DexHeader* pHeader)
1274{
1275 /*
1276 * Rewrite the checksum. We leave the SHA-1 signature alone.
1277 */
1278 uLong adler = adler32(0L, Z_NULL, 0);
1279 const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
1280
1281 adler = adler32(adler, addr + nonSum, len - nonSum);
1282 pHeader->checksum = adler;
1283}
1284
1285/*
1286 * Try to load all classes in the specified DEX. If they have some sort
1287 * of broken dependency, e.g. their superclass lives in a different DEX
1288 * that wasn't previously loaded into the bootstrap class path, loading
1289 * will fail. This is the desired behavior.
1290 *
1291 * We have no notion of class loader at this point, so we load all of
1292 * the classes with the bootstrap class loader. It turns out this has
1293 * exactly the behavior we want, and has no ill side effects because we're
1294 * running in a separate process and anything we load here will be forgotten.
1295 *
1296 * We set the CLASS_MULTIPLE_DEFS flag here if we see multiple definitions.
1297 * This works because we only call here as part of optimization / pre-verify,
1298 * not during verification as part of loading a class into a running VM.
1299 *
1300 * This returns "false" if the world is too screwed up to do anything
1301 * useful at all.
1302 */
1303static bool loadAllClasses(DvmDex* pDvmDex)
1304{
1305 u4 count = pDvmDex->pDexFile->pHeader->classDefsSize;
1306 u4 idx;
1307 int loaded = 0;
1308
1309 LOGV("DexOpt: +++ trying to load %d classes\n", count);
1310
1311 dvmSetBootPathExtraDex(pDvmDex);
1312
1313 /*
1314 * We have some circularity issues with Class and Object that are most
1315 * easily avoided by ensuring that Object is never the first thing we
1316 * try to find. Take care of that here. (We only need to do this when
1317 * loading classes from the DEX file that contains Object, and only
1318 * when Object comes first in the list, but it costs very little to
1319 * do it in all cases.)
1320 */
1321 if (dvmFindSystemClass("Ljava/lang/Class;") == NULL) {
1322 LOGE("ERROR: java.lang.Class does not exist!\n");
1323 return false;
1324 }
1325
1326 for (idx = 0; idx < count; idx++) {
1327 const DexClassDef* pClassDef;
1328 const char* classDescriptor;
1329 ClassObject* newClass;
1330
1331 pClassDef = dexGetClassDef(pDvmDex->pDexFile, idx);
1332 classDescriptor =
1333 dexStringByTypeIdx(pDvmDex->pDexFile, pClassDef->classIdx);
1334
1335 LOGV("+++ loading '%s'", classDescriptor);
1336 //newClass = dvmDefineClass(pDexFile, classDescriptor,
1337 // NULL);
1338 newClass = dvmFindSystemClassNoInit(classDescriptor);
1339 if (newClass == NULL) {
1340 LOGV("DexOpt: failed loading '%s'\n", classDescriptor);
1341 dvmClearOptException(dvmThreadSelf());
1342 } else if (newClass->pDvmDex != pDvmDex) {
1343 /*
1344 * We don't load the new one, and we tag the first one found
1345 * with the "multiple def" flag so the resolver doesn't try
1346 * to make it available.
1347 */
1348 LOGD("DexOpt: '%s' has an earlier definition; blocking out\n",
1349 classDescriptor);
1350 SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
1351 } else {
1352 loaded++;
1353 }
1354 }
1355 LOGV("DexOpt: +++ successfully loaded %d classes\n", loaded);
1356
1357 dvmSetBootPathExtraDex(NULL);
1358 return true;
1359}
1360
1361
1362/*
1363 * Create a table of inline substitutions.
1364 *
1365 * TODO: this is currently just a linear array. We will want to put this
1366 * into a hash table as the list size increases.
1367 */
1368static InlineSub* createInlineSubsTable(void)
1369{
1370 const InlineOperation* ops = dvmGetInlineOpsTable();
1371 const int count = dvmGetInlineOpsTableLength();
1372 InlineSub* table;
1373 Method* method;
1374 ClassObject* clazz;
1375 int i, tableIndex;
1376
1377 /*
1378 * Allocate for optimism: one slot per entry, plus an end-of-list marker.
1379 */
1380 table = malloc(sizeof(InlineSub) * (count+1));
1381
1382 tableIndex = 0;
1383 for (i = 0; i < count; i++) {
1384 clazz = dvmFindClassNoInit(ops[i].classDescriptor, NULL);
1385 if (clazz == NULL) {
1386 LOGV("DexOpt: can't inline for class '%s': not found\n",
1387 ops[i].classDescriptor);
1388 dvmClearOptException(dvmThreadSelf());
1389 } else {
1390 /*
1391 * Method could be virtual or direct. Try both. Don't use
1392 * the "hier" versions.
1393 */
1394 method = dvmFindDirectMethodByDescriptor(clazz, ops[i].methodName,
1395 ops[i].methodSignature);
1396 if (method == NULL)
1397 method = dvmFindVirtualMethodByDescriptor(clazz, ops[i].methodName,
1398 ops[i].methodSignature);
1399 if (method == NULL) {
1400 LOGW("DexOpt: can't inline %s.%s %s: method not found\n",
1401 ops[i].classDescriptor, ops[i].methodName,
1402 ops[i].methodSignature);
1403 } else {
1404 if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
1405 LOGW("DexOpt: WARNING: inline op on non-final class/method "
1406 "%s.%s\n",
1407 clazz->descriptor, method->name);
1408 /* fail? */
1409 }
1410 if (dvmIsSynchronizedMethod(method) ||
1411 dvmIsDeclaredSynchronizedMethod(method))
1412 {
1413 LOGW("DexOpt: WARNING: inline op on synchronized method "
1414 "%s.%s\n",
1415 clazz->descriptor, method->name);
1416 /* fail? */
1417 }
1418
1419 table[tableIndex].method = method;
1420 table[tableIndex].inlineIdx = i;
1421 tableIndex++;
1422
1423 LOGV("DexOpt: will inline %d: %s.%s %s\n", i,
1424 ops[i].classDescriptor, ops[i].methodName,
1425 ops[i].methodSignature);
1426 }
1427 }
1428 }
1429
1430 /* mark end of table */
1431 table[tableIndex].method = NULL;
1432 LOGV("DexOpt: inline table has %d entries\n", tableIndex);
1433
1434 return table;
1435}
1436
1437/*
1438 * Run through all classes that were successfully loaded from this DEX
1439 * file and optimize their code sections.
1440 */
1441static void optimizeLoadedClasses(DexFile* pDexFile)
1442{
1443 u4 count = pDexFile->pHeader->classDefsSize;
1444 u4 idx;
1445 InlineSub* inlineSubs = NULL;
1446
1447 LOGV("DexOpt: +++ optimizing up to %d classes\n", count);
1448 assert(gDvm.dexOptMode != OPTIMIZE_MODE_NONE);
1449
1450 inlineSubs = createInlineSubsTable();
1451
1452 for (idx = 0; idx < count; idx++) {
1453 const DexClassDef* pClassDef;
1454 const char* classDescriptor;
1455 ClassObject* clazz;
1456
1457 pClassDef = dexGetClassDef(pDexFile, idx);
1458 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
1459
1460 /* all classes are loaded into the bootstrap class loader */
1461 clazz = dvmLookupClass(classDescriptor, NULL, false);
1462 if (clazz != NULL) {
1463 if ((pClassDef->accessFlags & CLASS_ISPREVERIFIED) == 0 &&
1464 gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
1465 {
1466 LOGV("DexOpt: not optimizing '%s': not verified\n",
1467 classDescriptor);
1468 } else if (clazz->pDvmDex->pDexFile != pDexFile) {
1469 /* shouldn't be here -- verifier should have caught */
1470 LOGD("DexOpt: not optimizing '%s': multiple definitions\n",
1471 classDescriptor);
1472 } else {
1473 optimizeClass(clazz, inlineSubs);
1474
1475 /* set the flag whether or not we actually did anything */
1476 ((DexClassDef*)pClassDef)->accessFlags |=
1477 CLASS_ISOPTIMIZED;
1478 }
1479 } else {
1480 LOGV("DexOpt: not optimizing unavailable class '%s'\n",
1481 classDescriptor);
1482 }
1483 }
1484
1485 free(inlineSubs);
1486}
1487
1488/*
1489 * Optimize the specified class.
1490 */
1491static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs)
1492{
1493 int i;
1494
1495 for (i = 0; i < clazz->directMethodCount; i++) {
1496 if (!optimizeMethod(&clazz->directMethods[i], inlineSubs))
1497 goto fail;
1498 }
1499 for (i = 0; i < clazz->virtualMethodCount; i++) {
1500 if (!optimizeMethod(&clazz->virtualMethods[i], inlineSubs))
1501 goto fail;
1502 }
1503
1504 return;
1505
1506fail:
1507 LOGV("DexOpt: ceasing optimization attempts on %s\n", clazz->descriptor);
1508}
1509
1510/*
1511 * Optimize instructions in a method.
1512 *
1513 * Returns "true" if all went well, "false" if we bailed out early when
1514 * something failed.
1515 */
1516static bool optimizeMethod(Method* method, const InlineSub* inlineSubs)
1517{
1518 u4 insnsSize;
1519 u2* insns;
1520 u2 inst;
1521
1522 if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
1523 return true;
1524
1525 insns = (u2*) method->insns;
1526 assert(insns != NULL);
1527 insnsSize = dvmGetMethodInsnsSize(method);
1528
1529 while (insnsSize > 0) {
1530 int width;
1531
1532 inst = *insns & 0xff;
1533
1534 switch (inst) {
1535 case OP_IGET:
1536 case OP_IGET_BOOLEAN:
1537 case OP_IGET_BYTE:
1538 case OP_IGET_CHAR:
1539 case OP_IGET_SHORT:
1540 rewriteInstField(method, insns, OP_IGET_QUICK);
1541 break;
1542 case OP_IGET_WIDE:
1543 rewriteInstField(method, insns, OP_IGET_WIDE_QUICK);
1544 break;
1545 case OP_IGET_OBJECT:
1546 rewriteInstField(method, insns, OP_IGET_OBJECT_QUICK);
1547 break;
1548 case OP_IPUT:
1549 case OP_IPUT_BOOLEAN:
1550 case OP_IPUT_BYTE:
1551 case OP_IPUT_CHAR:
1552 case OP_IPUT_SHORT:
1553 rewriteInstField(method, insns, OP_IPUT_QUICK);
1554 break;
1555 case OP_IPUT_WIDE:
1556 rewriteInstField(method, insns, OP_IPUT_WIDE_QUICK);
1557 break;
1558 case OP_IPUT_OBJECT:
1559 rewriteInstField(method, insns, OP_IPUT_OBJECT_QUICK);
1560 break;
1561
1562 case OP_INVOKE_VIRTUAL:
1563 if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL,inlineSubs))
1564 {
1565 if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_VIRTUAL_QUICK))
1566 return false;
1567 }
1568 break;
1569 case OP_INVOKE_VIRTUAL_RANGE:
Andy McFaddenb0a05412009-11-19 10:23:41 -08001570 if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL,
1571 inlineSubs))
1572 {
1573 if (!rewriteVirtualInvoke(method, insns,
1574 OP_INVOKE_VIRTUAL_QUICK_RANGE))
1575 {
1576 return false;
1577 }
1578 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001579 break;
1580 case OP_INVOKE_SUPER:
1581 if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK))
1582 return false;
1583 break;
1584 case OP_INVOKE_SUPER_RANGE:
1585 if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE))
1586 return false;
1587 break;
1588
1589 case OP_INVOKE_DIRECT:
1590 if (!rewriteExecuteInline(method, insns, METHOD_DIRECT, inlineSubs))
1591 {
Andy McFaddenb0a05412009-11-19 10:23:41 -08001592 if (!rewriteEmptyDirectInvoke(method, insns))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001593 return false;
1594 }
1595 break;
Andy McFaddenb0a05412009-11-19 10:23:41 -08001596 case OP_INVOKE_DIRECT_RANGE:
1597 rewriteExecuteInlineRange(method, insns, METHOD_DIRECT, inlineSubs);
1598 break;
1599
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001600 case OP_INVOKE_STATIC:
1601 rewriteExecuteInline(method, insns, METHOD_STATIC, inlineSubs);
1602 break;
Andy McFaddenb0a05412009-11-19 10:23:41 -08001603 case OP_INVOKE_STATIC_RANGE:
1604 rewriteExecuteInlineRange(method, insns, METHOD_STATIC, inlineSubs);
1605 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001606
1607 default:
1608 // ignore this instruction
1609 ;
1610 }
1611
1612 if (*insns == kPackedSwitchSignature) {
1613 width = 4 + insns[1] * 2;
1614 } else if (*insns == kSparseSwitchSignature) {
1615 width = 2 + insns[1] * 4;
1616 } else if (*insns == kArrayDataSignature) {
1617 u2 elemWidth = insns[1];
1618 u4 len = insns[2] | (((u4)insns[3]) << 16);
1619 width = 4 + (elemWidth * len + 1) / 2;
1620 } else {
1621 width = dexGetInstrWidth(gDvm.instrWidth, inst);
1622 }
1623 assert(width > 0);
1624
1625 insns += width;
1626 insnsSize -= width;
1627 }
1628
1629 assert(insnsSize == 0);
1630 return true;
1631}
1632
1633
1634/*
1635 * If "referrer" and "resClass" don't come from the same DEX file, and
1636 * the DEX we're working on is not destined for the bootstrap class path,
1637 * tweak the class loader so package-access checks work correctly.
1638 *
1639 * Only do this if we're doing pre-verification or optimization.
1640 */
1641static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
1642{
1643 if (!gDvm.optimizing)
1644 return;
1645 assert(referrer->classLoader == NULL);
1646 assert(resClass->classLoader == NULL);
1647
1648 if (!gDvm.optimizingBootstrapClass) {
1649 /* class loader for an array class comes from element type */
1650 if (dvmIsArrayClass(resClass))
1651 resClass = resClass->elementClass;
1652 if (referrer->pDvmDex != resClass->pDvmDex)
1653 resClass->classLoader = (Object*) 0xdead3333;
1654 }
1655}
1656
1657/*
1658 * Undo the effects of tweakLoader.
1659 */
1660static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
1661{
1662 if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
1663 return;
1664
1665 if (dvmIsArrayClass(resClass))
1666 resClass = resClass->elementClass;
1667 resClass->classLoader = NULL;
1668}
1669
1670
1671/*
1672 * Alternate version of dvmResolveClass for use with verification and
1673 * optimization. Performs access checks on every resolve, and refuses
1674 * to acknowledge the existence of classes defined in more than one DEX
1675 * file.
1676 *
1677 * Exceptions caused by failures are cleared before returning.
Andy McFadden62a75162009-04-17 17:23:37 -07001678 *
1679 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001680 */
Andy McFadden62a75162009-04-17 17:23:37 -07001681ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
1682 VerifyError* pFailure)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001683{
1684 DvmDex* pDvmDex = referrer->pDvmDex;
1685 ClassObject* resClass;
1686
1687 /*
1688 * Check the table first. If not there, do the lookup by name.
1689 */
1690 resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
1691 if (resClass == NULL) {
1692 const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
1693 if (className[0] != '\0' && className[1] == '\0') {
1694 /* primitive type */
1695 resClass = dvmFindPrimitiveClass(className[0]);
1696 } else {
1697 resClass = dvmFindClassNoInit(className, referrer->classLoader);
1698 }
1699 if (resClass == NULL) {
1700 /* not found, exception should be raised */
1701 LOGV("DexOpt: class %d (%s) not found\n",
1702 classIdx,
1703 dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
Andy McFadden686e1e22009-05-26 16:56:30 -07001704 if (pFailure != NULL) {
1705 /* dig through the wrappers to find the original failure */
1706 Object* excep = dvmGetException(dvmThreadSelf());
1707 while (true) {
1708 Object* cause = dvmGetExceptionCause(excep);
1709 if (cause == NULL)
1710 break;
1711 excep = cause;
1712 }
1713 if (strcmp(excep->clazz->descriptor,
1714 "Ljava/lang/IncompatibleClassChangeError;") == 0)
1715 {
1716 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
1717 } else {
1718 *pFailure = VERIFY_ERROR_NO_CLASS;
1719 }
1720 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001721 dvmClearOptException(dvmThreadSelf());
1722 return NULL;
1723 }
1724
1725 /*
1726 * Add it to the resolved table so we're faster on the next lookup.
1727 */
1728 dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
1729 }
1730
1731 /* multiple definitions? */
1732 if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
1733 LOGI("DexOpt: not resolving ambiguous class '%s'\n",
1734 resClass->descriptor);
Andy McFadden62a75162009-04-17 17:23:37 -07001735 if (pFailure != NULL)
1736 *pFailure = VERIFY_ERROR_NO_CLASS;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001737 return NULL;
1738 }
1739
1740 /* access allowed? */
1741 tweakLoader(referrer, resClass);
1742 bool allowed = dvmCheckClassAccess(referrer, resClass);
1743 untweakLoader(referrer, resClass);
1744 if (!allowed) {
1745 LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
1746 referrer->descriptor, resClass->descriptor);
Andy McFadden62a75162009-04-17 17:23:37 -07001747 if (pFailure != NULL)
Andy McFaddenb51ea112009-05-08 16:50:17 -07001748 *pFailure = VERIFY_ERROR_ACCESS_CLASS;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001749 return NULL;
1750 }
1751
1752 return resClass;
1753}
1754
1755/*
1756 * Alternate version of dvmResolveInstField().
Andy McFadden62a75162009-04-17 17:23:37 -07001757 *
1758 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001759 */
Andy McFadden62a75162009-04-17 17:23:37 -07001760InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
1761 VerifyError* pFailure)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001762{
1763 DvmDex* pDvmDex = referrer->pDvmDex;
1764 InstField* resField;
1765
1766 resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
1767 if (resField == NULL) {
1768 const DexFieldId* pFieldId;
1769 ClassObject* resClass;
1770
1771 pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
1772
1773 /*
1774 * Find the field's class.
1775 */
Andy McFadden62a75162009-04-17 17:23:37 -07001776 resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001777 if (resClass == NULL) {
1778 //dvmClearOptException(dvmThreadSelf());
1779 assert(!dvmCheckException(dvmThreadSelf()));
Andy McFaddenb51ea112009-05-08 16:50:17 -07001780 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001781 return NULL;
1782 }
1783
Andy McFadden1d9206d2009-07-15 14:34:49 -07001784 resField = (InstField*)dvmFindFieldHier(resClass,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001785 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
1786 dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
1787 if (resField == NULL) {
1788 LOGD("DexOpt: couldn't find field %s.%s\n",
1789 resClass->descriptor,
1790 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
Andy McFadden62a75162009-04-17 17:23:37 -07001791 if (pFailure != NULL)
1792 *pFailure = VERIFY_ERROR_NO_FIELD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001793 return NULL;
1794 }
Andy McFadden1d9206d2009-07-15 14:34:49 -07001795 if (dvmIsStaticField(&resField->field)) {
1796 LOGD("DexOpt: wanted instance, got static for field %s.%s\n",
1797 resClass->descriptor,
1798 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
1799 if (pFailure != NULL)
1800 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
1801 return NULL;
1802 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001803
1804 /*
1805 * Add it to the resolved table so we're faster on the next lookup.
1806 */
1807 dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
1808 }
1809
1810 /* access allowed? */
1811 tweakLoader(referrer, resField->field.clazz);
1812 bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
1813 untweakLoader(referrer, resField->field.clazz);
1814 if (!allowed) {
1815 LOGI("DexOpt: access denied from %s to field %s.%s\n",
1816 referrer->descriptor, resField->field.clazz->descriptor,
1817 resField->field.name);
Andy McFadden62a75162009-04-17 17:23:37 -07001818 if (pFailure != NULL)
Andy McFaddenb51ea112009-05-08 16:50:17 -07001819 *pFailure = VERIFY_ERROR_ACCESS_FIELD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001820 return NULL;
1821 }
1822
1823 return resField;
1824}
1825
1826/*
1827 * Alternate version of dvmResolveStaticField().
1828 *
1829 * Does not force initialization of the resolved field's class.
Andy McFadden62a75162009-04-17 17:23:37 -07001830 *
1831 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001832 */
Andy McFadden62a75162009-04-17 17:23:37 -07001833StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
1834 VerifyError* pFailure)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001835{
1836 DvmDex* pDvmDex = referrer->pDvmDex;
1837 StaticField* resField;
1838
1839 resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
1840 if (resField == NULL) {
1841 const DexFieldId* pFieldId;
1842 ClassObject* resClass;
1843
1844 pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
1845
1846 /*
1847 * Find the field's class.
1848 */
Andy McFadden62a75162009-04-17 17:23:37 -07001849 resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001850 if (resClass == NULL) {
1851 //dvmClearOptException(dvmThreadSelf());
1852 assert(!dvmCheckException(dvmThreadSelf()));
Andy McFaddenb51ea112009-05-08 16:50:17 -07001853 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001854 return NULL;
1855 }
1856
Andy McFadden1d9206d2009-07-15 14:34:49 -07001857 resField = (StaticField*)dvmFindFieldHier(resClass,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001858 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
1859 dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
1860 if (resField == NULL) {
1861 LOGD("DexOpt: couldn't find static field\n");
Andy McFadden62a75162009-04-17 17:23:37 -07001862 if (pFailure != NULL)
1863 *pFailure = VERIFY_ERROR_NO_FIELD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001864 return NULL;
1865 }
Andy McFadden1d9206d2009-07-15 14:34:49 -07001866 if (!dvmIsStaticField(&resField->field)) {
1867 LOGD("DexOpt: wanted static, got instance for field %s.%s\n",
1868 resClass->descriptor,
1869 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
1870 if (pFailure != NULL)
1871 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
1872 return NULL;
1873 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001874
1875 /*
1876 * Add it to the resolved table so we're faster on the next lookup.
1877 *
1878 * We can only do this if we're in "dexopt", because the presence
1879 * of a valid value in the resolution table implies that the class
1880 * containing the static field has been initialized.
1881 */
1882 if (gDvm.optimizing)
1883 dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
1884 }
1885
1886 /* access allowed? */
1887 tweakLoader(referrer, resField->field.clazz);
1888 bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
1889 untweakLoader(referrer, resField->field.clazz);
1890 if (!allowed) {
1891 LOGI("DexOpt: access denied from %s to field %s.%s\n",
1892 referrer->descriptor, resField->field.clazz->descriptor,
1893 resField->field.name);
Andy McFadden62a75162009-04-17 17:23:37 -07001894 if (pFailure != NULL)
Andy McFaddenb51ea112009-05-08 16:50:17 -07001895 *pFailure = VERIFY_ERROR_ACCESS_FIELD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001896 return NULL;
1897 }
1898
1899 return resField;
1900}
1901
1902
1903/*
1904 * Rewrite an iget/iput instruction. These all have the form:
1905 * op vA, vB, field@CCCC
1906 *
1907 * Where vA holds the value, vB holds the object reference, and CCCC is
1908 * the field reference constant pool offset. We want to replace CCCC
1909 * with the byte offset from the start of the object.
1910 *
1911 * "clazz" is the referring class. We need this because we verify
1912 * access rights here.
1913 */
1914static void rewriteInstField(Method* method, u2* insns, OpCode newOpc)
1915{
1916 ClassObject* clazz = method->clazz;
1917 u2 fieldIdx = insns[1];
1918 InstField* field;
1919 int byteOffset;
1920
Andy McFadden62a75162009-04-17 17:23:37 -07001921 field = dvmOptResolveInstField(clazz, fieldIdx, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001922 if (field == NULL) {
1923 LOGI("DexOpt: unable to optimize field ref 0x%04x at 0x%02x in %s.%s\n",
1924 fieldIdx, (int) (insns - method->insns), clazz->descriptor,
1925 method->name);
1926 return;
1927 }
1928
1929 if (field->byteOffset >= 65536) {
1930 LOGI("DexOpt: field offset exceeds 64K (%d)\n", field->byteOffset);
1931 return;
1932 }
1933
1934 insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
1935 insns[1] = (u2) field->byteOffset;
1936 LOGVV("DexOpt: rewrote access to %s.%s --> %d\n",
1937 field->field.clazz->descriptor, field->field.name,
1938 field->byteOffset);
1939}
1940
1941/*
1942 * Alternate version of dvmResolveMethod().
1943 *
1944 * Doesn't throw exceptions, and checks access on every lookup.
Andy McFadden62a75162009-04-17 17:23:37 -07001945 *
1946 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001947 */
1948Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
Andy McFadden62a75162009-04-17 17:23:37 -07001949 MethodType methodType, VerifyError* pFailure)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001950{
1951 DvmDex* pDvmDex = referrer->pDvmDex;
1952 Method* resMethod;
1953
Andy McFadden1d9206d2009-07-15 14:34:49 -07001954 assert(methodType == METHOD_DIRECT ||
1955 methodType == METHOD_VIRTUAL ||
1956 methodType == METHOD_STATIC);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001957
1958 LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx,
1959 referrer->descriptor);
1960
1961 resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
1962 if (resMethod == NULL) {
1963 const DexMethodId* pMethodId;
1964 ClassObject* resClass;
1965
1966 pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
1967
Andy McFadden62a75162009-04-17 17:23:37 -07001968 resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001969 if (resClass == NULL) {
Andy McFaddenb51ea112009-05-08 16:50:17 -07001970 /*
1971 * Can't find the class that the method is a part of, or don't
1972 * have permission to access the class.
1973 */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001974 LOGV("DexOpt: can't find called method's class (?.%s)\n",
1975 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
Andy McFaddenb51ea112009-05-08 16:50:17 -07001976 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001977 return NULL;
1978 }
1979 if (dvmIsInterfaceClass(resClass)) {
1980 /* method is part of an interface; this is wrong method for that */
1981 LOGW("DexOpt: method is in an interface\n");
Andy McFadden62a75162009-04-17 17:23:37 -07001982 if (pFailure != NULL)
1983 *pFailure = VERIFY_ERROR_GENERIC;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001984 return NULL;
1985 }
1986
1987 /*
1988 * We need to chase up the class hierarchy to find methods defined
1989 * in super-classes. (We only want to check the current class
1990 * if we're looking for a constructor.)
1991 */
1992 DexProto proto;
1993 dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
1994
1995 if (methodType == METHOD_DIRECT) {
1996 resMethod = dvmFindDirectMethod(resClass,
1997 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001998 } else {
Andy McFadden1d9206d2009-07-15 14:34:49 -07001999 /* METHOD_STATIC or METHOD_VIRTUAL */
2000 resMethod = dvmFindMethodHier(resClass,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002001 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
2002 }
2003
2004 if (resMethod == NULL) {
2005 LOGV("DexOpt: couldn't find method '%s'\n",
2006 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
Andy McFadden62a75162009-04-17 17:23:37 -07002007 if (pFailure != NULL)
2008 *pFailure = VERIFY_ERROR_NO_METHOD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002009 return NULL;
2010 }
Andy McFadden1d9206d2009-07-15 14:34:49 -07002011 if (methodType == METHOD_STATIC) {
2012 if (!dvmIsStaticMethod(resMethod)) {
2013 LOGD("DexOpt: wanted static, got instance for method %s.%s\n",
2014 resClass->descriptor, resMethod->name);
2015 if (pFailure != NULL)
2016 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
2017 return NULL;
2018 }
2019 } else if (methodType == METHOD_VIRTUAL) {
2020 if (dvmIsStaticMethod(resMethod)) {
2021 LOGD("DexOpt: wanted instance, got static for method %s.%s\n",
2022 resClass->descriptor, resMethod->name);
2023 if (pFailure != NULL)
2024 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
2025 return NULL;
2026 }
2027 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002028
2029 /* see if this is a pure-abstract method */
2030 if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
2031 LOGW("DexOpt: pure-abstract method '%s' in %s\n",
2032 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
2033 resClass->descriptor);
Andy McFadden62a75162009-04-17 17:23:37 -07002034 if (pFailure != NULL)
2035 *pFailure = VERIFY_ERROR_GENERIC;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002036 return NULL;
2037 }
2038
2039 /*
2040 * Add it to the resolved table so we're faster on the next lookup.
2041 *
2042 * We can only do this for static methods if we're not in "dexopt",
2043 * because the presence of a valid value in the resolution table
2044 * implies that the class containing the static field has been
2045 * initialized.
2046 */
2047 if (methodType != METHOD_STATIC || gDvm.optimizing)
2048 dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
2049 }
2050
2051 LOGVV("--- found method %d (%s.%s)\n",
2052 methodIdx, resMethod->clazz->descriptor, resMethod->name);
2053
2054 /* access allowed? */
2055 tweakLoader(referrer, resMethod->clazz);
2056 bool allowed = dvmCheckMethodAccess(referrer, resMethod);
2057 untweakLoader(referrer, resMethod->clazz);
2058 if (!allowed) {
2059 IF_LOGI() {
2060 char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
2061 LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
2062 resMethod->clazz->descriptor, resMethod->name, desc,
2063 referrer->descriptor);
2064 free(desc);
2065 }
Andy McFadden62a75162009-04-17 17:23:37 -07002066 if (pFailure != NULL)
Andy McFaddenb51ea112009-05-08 16:50:17 -07002067 *pFailure = VERIFY_ERROR_ACCESS_METHOD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002068 return NULL;
2069 }
2070
2071 return resMethod;
2072}
2073
2074/*
2075 * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
2076 * invoke-super/range. These all have the form:
2077 * op vAA, meth@BBBB, reg stuff @CCCC
2078 *
2079 * We want to replace the method constant pool index BBBB with the
2080 * vtable index.
2081 */
2082static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc)
2083{
2084 ClassObject* clazz = method->clazz;
2085 Method* baseMethod;
2086 u2 methodIdx = insns[1];
2087
Andy McFadden62a75162009-04-17 17:23:37 -07002088 baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002089 if (baseMethod == NULL) {
2090 LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n",
2091 methodIdx,
2092 (int) (insns - method->insns), clazz->descriptor,
2093 method->name);
2094 return false;
2095 }
2096
2097 assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
2098 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
2099 (insns[0] & 0xff) == OP_INVOKE_SUPER ||
2100 (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
2101
2102 /*
2103 * Note: Method->methodIndex is a u2 and is range checked during the
2104 * initial load.
2105 */
2106 insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
2107 insns[1] = baseMethod->methodIndex;
2108
2109 //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
2110 // method->clazz->descriptor, method->name,
2111 // baseMethod->clazz->descriptor, baseMethod->name);
2112
2113 return true;
2114}
2115
2116/*
2117 * Rewrite invoke-direct, which has the form:
2118 * op vAA, meth@BBBB, reg stuff @CCCC
2119 *
2120 * There isn't a lot we can do to make this faster, but in some situations
2121 * we can make it go away entirely.
2122 *
2123 * This must only be used when the invoked method does nothing and has
2124 * no return value (the latter being very important for verification).
2125 */
Andy McFaddenb0a05412009-11-19 10:23:41 -08002126static bool rewriteEmptyDirectInvoke(Method* method, u2* insns)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002127{
2128 ClassObject* clazz = method->clazz;
2129 Method* calledMethod;
2130 u2 methodIdx = insns[1];
2131
Andy McFadden62a75162009-04-17 17:23:37 -07002132 calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002133 if (calledMethod == NULL) {
2134 LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n",
2135 methodIdx,
2136 (int) (insns - method->insns), clazz->descriptor,
2137 method->name);
2138 return false;
2139 }
2140
2141 /* TODO: verify that java.lang.Object() is actually empty! */
2142 if (calledMethod->clazz == gDvm.classJavaLangObject &&
2143 dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
2144 {
2145 /*
2146 * Replace with "empty" instruction. DO NOT disturb anything
2147 * else about it, as we want it to function the same as
2148 * OP_INVOKE_DIRECT when debugging is enabled.
2149 */
2150 assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
2151 insns[0] = (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY;
2152
2153 //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
2154 // method->clazz->descriptor, method->name,
2155 // calledMethod->clazz->descriptor, calledMethod->name);
2156 }
2157
2158 return true;
2159}
2160
2161/*
2162 * Resolve an interface method reference.
2163 *
Andy McFadden62a75162009-04-17 17:23:37 -07002164 * No method access check here -- interface methods are always public.
2165 *
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002166 * Returns NULL if the method was not found. Does not throw an exception.
2167 */
2168Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
2169{
2170 DvmDex* pDvmDex = referrer->pDvmDex;
2171 Method* resMethod;
2172 int i;
2173
2174 LOGVV("--- resolving interface method %d (referrer=%s)\n",
2175 methodIdx, referrer->descriptor);
2176
2177 resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
2178 if (resMethod == NULL) {
2179 const DexMethodId* pMethodId;
2180 ClassObject* resClass;
2181
2182 pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
2183
Andy McFadden62a75162009-04-17 17:23:37 -07002184 resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002185 if (resClass == NULL) {
2186 /* can't find the class that the method is a part of */
2187 dvmClearOptException(dvmThreadSelf());
2188 return NULL;
2189 }
2190 if (!dvmIsInterfaceClass(resClass)) {
2191 /* whoops */
2192 LOGI("Interface method not part of interface class\n");
2193 return NULL;
2194 }
2195
2196 const char* methodName =
2197 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
2198 DexProto proto;
2199 dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
2200
2201 LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
2202 methodName, methodSig, resClass->descriptor);
2203 resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
2204 if (resMethod == NULL) {
2205 /* scan superinterfaces and superclass interfaces */
2206 LOGVV("+++ did not resolve immediately\n");
2207 for (i = 0; i < resClass->iftableCount; i++) {
2208 resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
2209 methodName, &proto);
2210 if (resMethod != NULL)
2211 break;
2212 }
2213
2214 if (resMethod == NULL) {
2215 LOGVV("+++ unable to resolve method %s\n", methodName);
2216 return NULL;
2217 }
2218 } else {
2219 LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
2220 resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
2221 }
2222
2223 /* we're expecting this to be abstract */
2224 if (!dvmIsAbstractMethod(resMethod)) {
2225 char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
2226 LOGW("Found non-abstract interface method %s.%s %s\n",
2227 resMethod->clazz->descriptor, resMethod->name, desc);
2228 free(desc);
2229 return NULL;
2230 }
2231
2232 /*
2233 * Add it to the resolved table so we're faster on the next lookup.
2234 */
2235 dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
2236 }
2237
2238 LOGVV("--- found interface method %d (%s.%s)\n",
2239 methodIdx, resMethod->clazz->descriptor, resMethod->name);
2240
2241 /* interface methods are always public; no need to check access */
2242
2243 return resMethod;
2244}
Andy McFaddenb0a05412009-11-19 10:23:41 -08002245
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002246/*
2247 * See if the method being called can be rewritten as an inline operation.
2248 * Works for invoke-virtual, invoke-direct, and invoke-static.
2249 *
2250 * Returns "true" if we replace it.
2251 */
2252static bool rewriteExecuteInline(Method* method, u2* insns,
2253 MethodType methodType, const InlineSub* inlineSubs)
2254{
2255 ClassObject* clazz = method->clazz;
2256 Method* calledMethod;
2257 u2 methodIdx = insns[1];
2258
2259 //return false;
2260
Andy McFadden62a75162009-04-17 17:23:37 -07002261 calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002262 if (calledMethod == NULL) {
2263 LOGV("+++ DexOpt inline: can't find %d\n", methodIdx);
2264 return false;
2265 }
2266
2267 while (inlineSubs->method != NULL) {
2268 /*
2269 if (extra) {
2270 LOGI("comparing %p vs %p %s.%s %s\n",
2271 inlineSubs->method, calledMethod,
2272 inlineSubs->method->clazz->descriptor,
2273 inlineSubs->method->name,
2274 inlineSubs->method->signature);
2275 }
2276 */
2277 if (inlineSubs->method == calledMethod) {
2278 assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
2279 (insns[0] & 0xff) == OP_INVOKE_STATIC ||
2280 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
2281 insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE;
2282 insns[1] = (u2) inlineSubs->inlineIdx;
2283
2284 //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
2285 // method->clazz->descriptor, method->name,
2286 // calledMethod->clazz->descriptor, calledMethod->name);
2287 return true;
2288 }
2289
2290 inlineSubs++;
2291 }
2292
2293 return false;
2294}
2295
Andy McFaddenb0a05412009-11-19 10:23:41 -08002296/*
2297 * See if the method being called can be rewritten as an inline operation.
2298 * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range.
2299 *
2300 * Returns "true" if we replace it.
2301 */
2302static bool rewriteExecuteInlineRange(Method* method, u2* insns,
2303 MethodType methodType, const InlineSub* inlineSubs)
2304{
2305 ClassObject* clazz = method->clazz;
2306 Method* calledMethod;
2307 u2 methodIdx = insns[1];
2308
2309 calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
2310 if (calledMethod == NULL) {
2311 LOGV("+++ DexOpt inline/range: can't find %d\n", methodIdx);
2312 return false;
2313 }
2314
2315 while (inlineSubs->method != NULL) {
2316 if (inlineSubs->method == calledMethod) {
2317 assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
2318 (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
2319 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
2320 insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE;
2321 insns[1] = (u2) inlineSubs->inlineIdx;
2322
2323 //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
2324 // method->clazz->descriptor, method->name,
2325 // calledMethod->clazz->descriptor, calledMethod->name);
2326 return true;
2327 }
2328
2329 inlineSubs++;
2330 }
2331
2332 return false;
2333}
2334