Rearrange some things.
This splits DexOptimize into DexPrepare (which deals with file shuffling
and fork/exec) and Optimize (which does the actual quickening of
instructions). The Optimize functions are now effectively private to
the "analysis" directory.
Twiddled some comments.
No substantive code changes.
Change-Id: Ia51865b259fb32822132e2373997866e360ca86a
diff --git a/libdex/InstrUtils.h b/libdex/InstrUtils.h
index 0c4e162..60244a8 100644
--- a/libdex/InstrUtils.h
+++ b/libdex/InstrUtils.h
@@ -90,8 +90,8 @@
typedef signed char InstructionWidth;
/*
- * Instruction flags, used by the verifier to determine where control
- * can flow to next.
+ * Instruction flags, used by the verifier and JIT to determine where
+ * control can flow to next. Expected to fit in 8 bits.
*/
typedef unsigned char InstructionFlags;
enum InstructionFlags {
diff --git a/vm/Dalvik.h b/vm/Dalvik.h
index 054b838..27fa316 100644
--- a/vm/Dalvik.h
+++ b/vm/Dalvik.h
@@ -67,7 +67,7 @@
#include "JniInternal.h"
#include "LinearAlloc.h"
#include "analysis/DexVerify.h"
-#include "analysis/DexOptimize.h"
+#include "analysis/DexPrepare.h"
#include "analysis/RegisterMap.h"
#include "Init.h"
#include "libdex/OpCode.h"
diff --git a/vm/Dvm.mk b/vm/Dvm.mk
index cf0d0c9..a059b41 100644
--- a/vm/Dvm.mk
+++ b/vm/Dvm.mk
@@ -138,8 +138,9 @@
alloc/DdmHeap.c \
alloc/Verify.c \
analysis/CodeVerify.c \
- analysis/DexOptimize.c \
+ analysis/DexPrepare.c \
analysis/DexVerify.c \
+ analysis/Optimize.c \
analysis/ReduceConstants.c \
analysis/RegisterMap.c \
analysis/VerifySubs.c \
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index 8187354..91f5414 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -29,6 +29,7 @@
*/
#include "Dalvik.h"
#include "analysis/CodeVerify.h"
+#include "analysis/Optimize.h"
#include "analysis/RegisterMap.h"
#include "libdex/DexCatch.h"
#include "libdex/InstrUtils.h"
@@ -5561,7 +5562,8 @@
/*
* If we didn't just set the result register, clear it out. This
* ensures that you can only use "move-result" immediately after the
- * result is set.
+ * result is set. (We could check this statically, but it's not
+ * expensive and it makes our debugging output cleaner.)
*/
if (!justSetResult) {
int reg = RESULT_REGISTER(insnRegCount);
diff --git a/vm/analysis/DexOptimize.c b/vm/analysis/DexOptimize.c
deleted file mode 100644
index ae1edfc..0000000
--- a/vm/analysis/DexOptimize.c
+++ /dev/null
@@ -1,2383 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Convert the output from "dx" into a locally-optimized DEX file.
- *
- * TODO: the format of the optimized header is currently "whatever we
- * happen to write", since the VM that writes it is by definition the same
- * as the VM that reads it. Still, it should be better documented and
- * more rigorously structured.
- */
-#include "Dalvik.h"
-#include "libdex/InstrUtils.h"
-#include "libdex/OptInvocation.h"
-#include "analysis/RegisterMap.h"
-
-#include <zlib.h>
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/file.h>
-#include <sys/wait.h>
-#include <fcntl.h>
-#include <errno.h>
-
-/*
- * Virtual/direct calls to "method" are replaced with an execute-inline
- * instruction with index "idx".
- */
-typedef struct InlineSub {
- Method* method;
- int inlineIdx;
-} InlineSub;
-
-
-/* fwd */
-static int writeDependencies(int fd, u4 modWhen, u4 crc);
-static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,\
- const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder);
-static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
- int err);
-static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum);
-
-static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,\
- u4* pHeaderFlags, DexClassLookup** ppClassLookup);
-static void updateChecksum(u1* addr, int len, DexHeader* pHeader);
-static bool loadAllClasses(DvmDex* pDvmDex);
-static void optimizeLoadedClasses(DexFile* pDexFile);
-static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs);
-static bool optimizeMethod(Method* method, const InlineSub* inlineSubs);
-static void rewriteInstField(Method* method, u2* insns, OpCode newOpc);
-static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc);
-static bool rewriteEmptyDirectInvoke(Method* method, u2* insns);
-static bool rewriteExecuteInline(Method* method, u2* insns,
- MethodType methodType, const InlineSub* inlineSubs);
-static bool rewriteExecuteInlineRange(Method* method, u2* insns,
- MethodType methodType, const InlineSub* inlineSubs);
-
-
-/*
- * Return the fd of an open file in the DEX file cache area. If the cache
- * file doesn't exist or is out of date, this will remove the old entry,
- * create a new one (writing only the file header), and return with the
- * "new file" flag set.
- *
- * It's possible to execute from an unoptimized DEX file directly,
- * assuming the byte ordering and structure alignment is correct, but
- * disadvantageous because some significant optimizations are not possible.
- * It's not generally possible to do the same from an uncompressed Jar
- * file entry, because we have to guarantee 32-bit alignment in the
- * memory-mapped file.
- *
- * For a Jar/APK file (a zip archive with "classes.dex" inside), "modWhen"
- * and "crc32" come from the Zip directory entry. For a stand-alone DEX
- * file, it's the modification date of the file and the Adler32 from the
- * DEX header (which immediately follows the magic). If these don't
- * match what's stored in the opt header, we reject the file immediately.
- *
- * On success, the file descriptor will be positioned just past the "opt"
- * file header, and will be locked with flock. "*pCachedName" will point
- * to newly-allocated storage.
- */
-int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
- u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
-{
- int fd, cc;
- struct stat fdStat, fileStat;
- bool readOnly = false;
-
- *pNewFile = false;
-
-retry:
- /*
- * Try to open the cache file. If we've been asked to,
- * create it if it doesn't exist.
- */
- fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
- if (fd < 0) {
- fd = open(cacheFileName, O_RDONLY, 0);
- if (fd < 0) {
- if (createIfMissing) {
- LOGE("Can't open dex cache '%s': %s\n",
- cacheFileName, strerror(errno));
- }
- return fd;
- }
- readOnly = true;
- }
-
- /*
- * Grab an exclusive lock on the cache file. If somebody else is
- * working on it, we'll block here until they complete. Because
- * we're waiting on an external resource, we go into VMWAIT mode.
- */
- int oldStatus;
- LOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)\n",
- cacheFileName, fd, isBootstrap);
- oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
- cc = flock(fd, LOCK_EX | LOCK_NB);
- if (cc != 0) {
- LOGD("DexOpt: sleeping on flock(%s)\n", cacheFileName);
- cc = flock(fd, LOCK_EX);
- }
- dvmChangeStatus(NULL, oldStatus);
- if (cc != 0) {
- LOGE("Can't lock dex cache '%s': %d\n", cacheFileName, cc);
- close(fd);
- return -1;
- }
- LOGV("DexOpt: locked cache file\n");
-
- /*
- * Check to see if the fd we opened and locked matches the file in
- * the filesystem. If they don't, then somebody else unlinked ours
- * and created a new file, and we need to use that one instead. (If
- * we caught them between the unlink and the create, we'll get an
- * ENOENT from the file stat.)
- */
- cc = fstat(fd, &fdStat);
- if (cc != 0) {
- LOGE("Can't stat open file '%s'\n", cacheFileName);
- LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
- goto close_fail;
- }
- cc = stat(cacheFileName, &fileStat);
- if (cc != 0 ||
- fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
- {
- LOGD("DexOpt: our open cache file is stale; sleeping and retrying\n");
- LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
- flock(fd, LOCK_UN);
- close(fd);
- usleep(250 * 1000); /* if something is hosed, don't peg machine */
- goto retry;
- }
-
- /*
- * We have the correct file open and locked. If the file size is zero,
- * then it was just created by us, and we want to fill in some fields
- * in the "opt" header and set "*pNewFile". Otherwise, we want to
- * verify that the fields in the header match our expectations, and
- * reset the file if they don't.
- */
- if (fdStat.st_size == 0) {
- if (readOnly) {
- LOGW("DexOpt: file has zero length and isn't writable\n");
- goto close_fail;
- }
- cc = dexOptCreateEmptyHeader(fd);
- if (cc != 0)
- goto close_fail;
- *pNewFile = true;
- LOGV("DexOpt: successfully initialized new cache file\n");
- } else {
- bool expectVerify, expectOpt;
-
- if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
- expectVerify = false;
- else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
- expectVerify = !isBootstrap;
- else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
- expectVerify = true;
-
- if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
- expectOpt = false;
- else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
- expectOpt = expectVerify;
- else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
- expectOpt = true;
-
- LOGV("checking deps, expecting vfy=%d opt=%d\n",
- expectVerify, expectOpt);
-
- if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
- expectVerify, expectOpt))
- {
- if (readOnly) {
- /*
- * We could unlink and rewrite the file if we own it or
- * the "sticky" bit isn't set on the directory. However,
- * we're not able to truncate it, which spoils things. So,
- * give up now.
- */
- if (createIfMissing) {
- LOGW("Cached DEX '%s' (%s) is stale and not writable\n",
- fileName, cacheFileName);
- }
- goto close_fail;
- }
-
- /*
- * If we truncate the existing file before unlinking it, any
- * process that has it mapped will fail when it tries to touch
- * the pages.
- *
- * This is very important. The zygote process will have the
- * boot DEX files (core, framework, etc.) mapped early. If
- * (say) core.dex gets updated, and somebody launches an app
- * that uses App.dex, then App.dex gets reoptimized because it's
- * dependent upon the boot classes. However, dexopt will be
- * using the *new* core.dex to do the optimizations, while the
- * app will actually be running against the *old* core.dex
- * because it starts from zygote.
- *
- * Even without zygote, it's still possible for a class loader
- * to pull in an APK that was optimized against an older set
- * of DEX files. We must ensure that everything fails when a
- * boot DEX gets updated, and for general "why aren't my
- * changes doing anything" purposes its best if we just make
- * everything crash when a DEX they're using gets updated.
- */
- LOGD("Stale deps in cache file; removing and retrying\n");
- if (ftruncate(fd, 0) != 0) {
- LOGW("Warning: unable to truncate cache file '%s': %s\n",
- cacheFileName, strerror(errno));
- /* keep going */
- }
- if (unlink(cacheFileName) != 0) {
- LOGW("Warning: unable to remove cache file '%s': %d %s\n",
- cacheFileName, errno, strerror(errno));
- /* keep going; permission failure should probably be fatal */
- }
- LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
- flock(fd, LOCK_UN);
- close(fd);
- goto retry;
- } else {
- LOGV("DexOpt: good deps in cache file\n");
- }
- }
-
- assert(fd >= 0);
- return fd;
-
-close_fail:
- flock(fd, LOCK_UN);
- close(fd);
- return -1;
-}
-
-/*
- * Unlock the file descriptor.
- *
- * Returns "true" on success.
- */
-bool dvmUnlockCachedDexFile(int fd)
-{
- LOGVV("DexOpt: unlocking cache file fd=%d\n", fd);
- return (flock(fd, LOCK_UN) == 0);
-}
-
-
-/*
- * Given a descriptor for a file with DEX data in it, produce an
- * optimized version.
- *
- * The file pointed to by "fd" is expected to be a locked shared resource
- * (or private); we make no efforts to enforce multi-process correctness
- * here.
- *
- * "fileName" is only used for debug output. "modWhen" and "crc" are stored
- * in the dependency set.
- *
- * The "isBootstrap" flag determines how the optimizer and verifier handle
- * package-scope access checks. When optimizing, we only load the bootstrap
- * class DEX files and the target DEX, so the flag determines whether the
- * target DEX classes are given a (synthetic) non-NULL classLoader pointer.
- * This only really matters if the target DEX contains classes that claim to
- * be in the same package as bootstrap classes.
- *
- * The optimizer will need to load every class in the target DEX file.
- * This is generally undesirable, so we start a subprocess to do the
- * work and wait for it to complete.
- *
- * Returns "true" on success. All data will have been written to "fd".
- */
-bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
- const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
-{
- const char* lastPart = strrchr(fileName, '/');
- if (lastPart != NULL)
- lastPart++;
- else
- lastPart = fileName;
-
- /*
- * For basic optimizations (byte-swapping and structure aligning) we
- * don't need to fork(). It looks like fork+exec is causing problems
- * with gdb on our bewildered Linux distro, so in some situations we
- * want to avoid this.
- *
- * For optimization and/or verification, we need to load all the classes.
- *
- * We don't check gDvm.generateRegisterMaps, since that is dependent
- * upon the verifier state.
- */
- if (gDvm.classVerifyMode == VERIFY_MODE_NONE &&
- (gDvm.dexOptMode == OPTIMIZE_MODE_NONE ||
- gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED))
- {
- LOGD("DexOpt: --- BEGIN (quick) '%s' ---\n", lastPart);
- return dvmContinueOptimization(fd, dexOffset, dexLength,
- fileName, modWhen, crc, isBootstrap);
- }
-
-
- LOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---\n", lastPart, isBootstrap);
-
- pid_t pid;
-
- /*
- * This could happen if something in our bootclasspath, which we thought
- * was all optimized, got rejected.
- */
- if (gDvm.optimizing) {
- LOGW("Rejecting recursive optimization attempt on '%s'\n", fileName);
- return false;
- }
-
- pid = fork();
- if (pid == 0) {
- static const int kUseValgrind = 0;
- static const char* kDexOptBin = "/bin/dexopt";
- static const char* kValgrinder = "/usr/bin/valgrind";
- static const int kFixedArgCount = 10;
- static const int kValgrindArgCount = 5;
- static const int kMaxIntLen = 12; // '-'+10dig+'\0' -OR- 0x+8dig
- int bcpSize = dvmGetBootPathSize();
- int argc = kFixedArgCount + bcpSize
- + (kValgrindArgCount * kUseValgrind);
- char* argv[argc+1]; // last entry is NULL
- char values[argc][kMaxIntLen];
- char* execFile;
- char* androidRoot;
- int flags;
-
- /* change process groups, so we don't clash with ProcessManager */
- setpgid(0, 0);
-
- /* full path to optimizer */
- androidRoot = getenv("ANDROID_ROOT");
- if (androidRoot == NULL) {
- LOGW("ANDROID_ROOT not set, defaulting to /system\n");
- androidRoot = "/system";
- }
- execFile = malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
- strcpy(execFile, androidRoot);
- strcat(execFile, kDexOptBin);
-
- /*
- * Create arg vector.
- */
- int curArg = 0;
-
- if (kUseValgrind) {
- /* probably shouldn't ship the hard-coded path */
- argv[curArg++] = (char*)kValgrinder;
- argv[curArg++] = "--tool=memcheck";
- argv[curArg++] = "--leak-check=yes"; // check for leaks too
- argv[curArg++] = "--leak-resolution=med"; // increase from 2 to 4
- argv[curArg++] = "--num-callers=16"; // default is 12
- assert(curArg == kValgrindArgCount);
- }
- argv[curArg++] = execFile;
-
- argv[curArg++] = "--dex";
-
- sprintf(values[2], "%d", DALVIK_VM_BUILD);
- argv[curArg++] = values[2];
-
- sprintf(values[3], "%d", fd);
- argv[curArg++] = values[3];
-
- sprintf(values[4], "%d", (int) dexOffset);
- argv[curArg++] = values[4];
-
- sprintf(values[5], "%d", (int) dexLength);
- argv[curArg++] = values[5];
-
- argv[curArg++] = (char*)fileName;
-
- sprintf(values[7], "%d", (int) modWhen);
- argv[curArg++] = values[7];
-
- sprintf(values[8], "%d", (int) crc);
- argv[curArg++] = values[8];
-
- flags = 0;
- if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE) {
- flags |= DEXOPT_OPT_ENABLED;
- if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
- flags |= DEXOPT_OPT_ALL;
- }
- if (gDvm.classVerifyMode != VERIFY_MODE_NONE) {
- flags |= DEXOPT_VERIFY_ENABLED;
- if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
- flags |= DEXOPT_VERIFY_ALL;
- }
- if (isBootstrap)
- flags |= DEXOPT_IS_BOOTSTRAP;
- if (gDvm.generateRegisterMaps)
- flags |= DEXOPT_GEN_REGISTER_MAP;
- sprintf(values[9], "%d", flags);
- argv[curArg++] = values[9];
-
- assert(((!kUseValgrind && curArg == kFixedArgCount) ||
- ((kUseValgrind && curArg == kFixedArgCount+kValgrindArgCount))));
-
- ClassPathEntry* cpe;
- for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
- argv[curArg++] = cpe->fileName;
- }
- assert(curArg == argc);
-
- argv[curArg] = NULL;
-
- if (kUseValgrind)
- execv(kValgrinder, argv);
- else
- execv(execFile, argv);
-
- LOGE("execv '%s'%s failed: %s\n", execFile,
- kUseValgrind ? " [valgrind]" : "", strerror(errno));
- exit(1);
- } else {
- LOGV("DexOpt: waiting for verify+opt, pid=%d\n", (int) pid);
- int status;
- pid_t gotPid;
- int oldStatus;
-
- /*
- * Wait for the optimization process to finish. We go into VMWAIT
- * mode here so GC suspension won't have to wait for us.
- */
- oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
- while (true) {
- gotPid = waitpid(pid, &status, 0);
- if (gotPid == -1 && errno == EINTR) {
- LOGD("waitpid interrupted, retrying\n");
- } else {
- break;
- }
- }
- dvmChangeStatus(NULL, oldStatus);
- if (gotPid != pid) {
- LOGE("waitpid failed: wanted %d, got %d: %s\n",
- (int) pid, (int) gotPid, strerror(errno));
- return false;
- }
-
- if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
- LOGD("DexOpt: --- END '%s' (success) ---\n", lastPart);
- return true;
- } else {
- LOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed\n",
- lastPart, status);
- return false;
- }
- }
-}
-
-/*
- * Do the actual optimization. This is called directly for "minimal"
- * optimization, or from a newly-created process for "full" optimization.
- *
- * For best use of disk/memory, we want to extract once and perform
- * optimizations in place. If the file has to expand or contract
- * to match local structure padding/alignment expectations, we want
- * to do the rewrite as part of the extract, rather than extracting
- * into a temp file and slurping it back out. (The structure alignment
- * is currently correct for all platforms, and this isn't expected to
- * change, so we should be okay with having it already extracted.)
- *
- * Returns "true" on success.
- */
-bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
- const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
-{
- DexClassLookup* pClassLookup = NULL;
- IndexMapSet* pIndexMapSet = NULL;
- RegisterMapBuilder* pRegMapBuilder = NULL;
- bool doVerify, doOpt;
- u4 headerFlags = 0;
-
- if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
- doVerify = false;
- else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
- doVerify = !isBootstrap;
- else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
- doVerify = true;
-
- if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
- doOpt = false;
- else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
- doOpt = doVerify;
- else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
- doOpt = true;
-
- LOGV("Continuing optimization (%s, isb=%d, vfy=%d, opt=%d)\n",
- fileName, isBootstrap, doVerify, doOpt);
-
- assert(dexOffset >= 0);
-
- /* quick test so we don't blow up on empty file */
- if (dexLength < (int) sizeof(DexHeader)) {
- LOGE("too small to be DEX\n");
- return false;
- }
- if (dexOffset < (int) sizeof(DexOptHeader)) {
- LOGE("not enough room for opt header\n");
- return false;
- }
-
- bool result = false;
-
- /*
- * Drop this into a global so we don't have to pass it around. We could
- * also add a field to DexFile, but since it only pertains to DEX
- * creation that probably doesn't make sense.
- */
- gDvm.optimizingBootstrapClass = isBootstrap;
-
- {
- /*
- * Map the entire file (so we don't have to worry about page
- * alignment). The expectation is that the output file contains
- * our DEX data plus room for a small header.
- */
- bool success;
- void* mapAddr;
- mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,
- MAP_SHARED, fd, 0);
- if (mapAddr == MAP_FAILED) {
- LOGE("unable to mmap DEX cache: %s\n", strerror(errno));
- goto bail;
- }
-
- /*
- * Rewrite the file. Byte reordering, structure realigning,
- * class verification, and bytecode optimization are all performed
- * here.
- *
- * In theory the file could change size and bits could shift around.
- * In practice this would be annoying to deal with, so the file
- * layout is designed so that it can always be rewritten in place.
- *
- * This sets "headerFlags" and creates the class lookup table as
- * part of doing the processing.
- */
- success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
- doVerify, doOpt, &headerFlags, &pClassLookup);
-
- if (success) {
- DvmDex* pDvmDex = NULL;
- u1* dexAddr = ((u1*) mapAddr) + dexOffset;
-
- if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
- LOGE("Unable to create DexFile\n");
- success = false;
- } else {
- /*
- * If configured to do so, scan the instructions, looking
- * for ways to reduce the size of the resolved-constant table.
- * This is done post-optimization, across the instructions
- * in all methods in all classes (even the ones that failed
- * to load).
- */
- pIndexMapSet = dvmRewriteConstants(pDvmDex);
-
- /*
- * If configured to do so, generate a full set of register
- * maps for all verified classes.
- */
- if (gDvm.generateRegisterMaps) {
- pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);
- if (pRegMapBuilder == NULL) {
- LOGE("Failed generating register maps\n");
- success = false;
- }
- }
-
- DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;
- updateChecksum(dexAddr, dexLength, pHeader);
-
- dvmDexFileFree(pDvmDex);
- }
- }
-
- /* unmap the read-write version, forcing writes to disk */
- if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) {
- LOGW("msync failed: %s\n", strerror(errno));
- // weird, but keep going
- }
-#if 1
- /*
- * This causes clean shutdown to fail, because we have loaded classes
- * that point into it. For the optimizer this isn't a problem,
- * because it's more efficient for the process to simply exit.
- * Exclude this code when doing clean shutdown for valgrind.
- */
- if (munmap(mapAddr, dexOffset + dexLength) != 0) {
- LOGE("munmap failed: %s\n", strerror(errno));
- goto bail;
- }
-#endif
-
- if (!success)
- goto bail;
- }
-
- /* get start offset, and adjust deps start for 64-bit alignment */
- off_t depsOffset, auxOffset, endOffset, adjOffset;
- int depsLength, auxLength;
- u4 optChecksum;
-
- depsOffset = lseek(fd, 0, SEEK_END);
- if (depsOffset < 0) {
- LOGE("lseek to EOF failed: %s\n", strerror(errno));
- goto bail;
- }
- adjOffset = (depsOffset + 7) & ~(0x07);
- if (adjOffset != depsOffset) {
- LOGV("Adjusting deps start from %d to %d\n",
- (int) depsOffset, (int) adjOffset);
- depsOffset = adjOffset;
- lseek(fd, depsOffset, SEEK_SET);
- }
-
- /*
- * Append the dependency list.
- */
- if (writeDependencies(fd, modWhen, crc) != 0) {
- LOGW("Failed writing dependencies\n");
- goto bail;
- }
-
- /* compute deps length, then adjust aux start for 64-bit alignment */
- auxOffset = lseek(fd, 0, SEEK_END);
- depsLength = auxOffset - depsOffset;
-
- adjOffset = (auxOffset + 7) & ~(0x07);
- if (adjOffset != auxOffset) {
- LOGV("Adjusting aux start from %d to %d\n",
- (int) auxOffset, (int) adjOffset);
- auxOffset = adjOffset;
- lseek(fd, auxOffset, SEEK_SET);
- }
-
- /*
- * Append any auxillary pre-computed data structures.
- */
- if (!writeAuxData(fd, pClassLookup, pIndexMapSet, pRegMapBuilder)) {
- LOGW("Failed writing aux data\n");
- goto bail;
- }
-
- endOffset = lseek(fd, 0, SEEK_END);
- auxLength = endOffset - auxOffset;
-
- /* compute checksum from start of deps to end of aux area */
- if (!computeFileChecksum(fd, depsOffset,
- (auxOffset+auxLength) - depsOffset, &optChecksum))
- {
- goto bail;
- }
-
- /*
- * Output the "opt" header with all values filled in and a correct
- * magic number.
- */
- DexOptHeader optHdr;
- memset(&optHdr, 0xff, sizeof(optHdr));
- memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);
- memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);
- optHdr.dexOffset = (u4) dexOffset;
- optHdr.dexLength = (u4) dexLength;
- optHdr.depsOffset = (u4) depsOffset;
- optHdr.depsLength = (u4) depsLength;
- optHdr.auxOffset = (u4) auxOffset;
- optHdr.auxLength = (u4) auxLength;
-
- optHdr.flags = headerFlags;
- optHdr.checksum = optChecksum;
-
- ssize_t actual;
- lseek(fd, 0, SEEK_SET);
- actual = write(fd, &optHdr, sizeof(optHdr));
- if (actual != sizeof(optHdr)) {
- logFailedWrite(sizeof(optHdr), actual, "opt header", errno);
- goto bail;
- }
-
- LOGV("Successfully wrote DEX header\n");
- result = true;
-
- //dvmRegisterMapDumpStats();
-
-bail:
- dvmFreeIndexMapSet(pIndexMapSet);
- dvmFreeRegisterMapBuilder(pRegMapBuilder);
- free(pClassLookup);
- return result;
-}
-
-
-/*
- * Get the cache file name from a ClassPathEntry.
- */
-static const char* getCacheFileName(const ClassPathEntry* cpe)
-{
- switch (cpe->kind) {
- case kCpeJar:
- return dvmGetJarFileCacheFileName((JarFile*) cpe->ptr);
- case kCpeDex:
- return dvmGetRawDexFileCacheFileName((RawDexFile*) cpe->ptr);
- default:
- LOGE("DexOpt: unexpected cpe kind %d\n", cpe->kind);
- dvmAbort();
- return NULL;
- }
-}
-
-/*
- * Get the SHA-1 signature.
- */
-static const u1* getSignature(const ClassPathEntry* cpe)
-{
- DvmDex* pDvmDex;
-
- switch (cpe->kind) {
- case kCpeJar:
- pDvmDex = dvmGetJarFileDex((JarFile*) cpe->ptr);
- break;
- case kCpeDex:
- pDvmDex = dvmGetRawDexFileDex((RawDexFile*) cpe->ptr);
- break;
- default:
- LOGE("unexpected cpe kind %d\n", cpe->kind);
- dvmAbort();
- pDvmDex = NULL; // make gcc happy
- }
-
- assert(pDvmDex != NULL);
- return pDvmDex->pDexFile->pHeader->signature;
-}
-
-
-/*
- * Dependency layout:
- * 4b Source file modification time, in seconds since 1970 UTC
- * 4b CRC-32 from Zip entry, or Adler32 from source DEX header
- * 4b Dalvik VM build number
- * 4b Number of dependency entries that follow
- * Dependency entries:
- * 4b Name length (including terminating null)
- * var Full path of cache entry (null terminated)
- * 20b SHA-1 signature from source DEX file
- *
- * If this changes, update DEX_OPT_MAGIC_VERS.
- */
-static const size_t kMinDepSize = 4 * 4;
-static const size_t kMaxDepSize = 4 * 4 + 1024; // sanity check
-
-/*
- * Read the "opt" header, verify it, then read the dependencies section
- * and verify that data as well.
- *
- * If "sourceAvail" is "true", this will verify that "modWhen" and "crc"
- * match up with what is stored in the header. If they don't, we reject
- * the file so that it can be recreated from the updated original. If
- * "sourceAvail" isn't set, e.g. for a .odex file, we ignore these arguments.
- *
- * On successful return, the file will be seeked immediately past the
- * "opt" header.
- */
-bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
- u4 crc, bool expectVerify, bool expectOpt)
-{
- DexOptHeader optHdr;
- u1* depData = NULL;
- const u1* magic;
- off_t posn;
- int result = false;
- ssize_t actual;
-
- /*
- * Start at the start. The "opt" header, when present, will always be
- * the first thing in the file.
- */
- if (lseek(fd, 0, SEEK_SET) != 0) {
- LOGE("DexOpt: failed to seek to start of file: %s\n", strerror(errno));
- goto bail;
- }
-
- /*
- * Read and do trivial verification on the opt header. The header is
- * always in host byte order.
- */
- if (read(fd, &optHdr, sizeof(optHdr)) != sizeof(optHdr)) {
- LOGE("DexOpt: failed reading opt header: %s\n", strerror(errno));
- goto bail;
- }
-
- magic = optHdr.magic;
- if (memcmp(magic, DEX_OPT_MAGIC, 4) != 0) {
- /* not a DEX file, or previous attempt was interrupted */
- LOGD("DexOpt: incorrect opt magic number (0x%02x %02x %02x %02x)\n",
- magic[0], magic[1], magic[2], magic[3]);
- goto bail;
- }
- if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
- LOGW("DexOpt: stale opt version (0x%02x %02x %02x %02x)\n",
- magic[4], magic[5], magic[6], magic[7]);
- goto bail;
- }
- if (optHdr.depsLength < kMinDepSize || optHdr.depsLength > kMaxDepSize) {
- LOGW("DexOpt: weird deps length %d, bailing\n", optHdr.depsLength);
- goto bail;
- }
-
- /*
- * Do the header flags match up with what we want?
- *
- * This is useful because it allows us to automatically regenerate
- * a file when settings change (e.g. verification is now mandatory),
- * but can cause difficulties if the bootstrap classes we depend upon
- * were handled differently than the current options specify. We get
- * upset because they're not verified or optimized, but we're not able
- * to regenerate them because the installer won't let us.
- *
- * (This is also of limited value when !sourceAvail.)
- *
- * So, for now, we essentially ignore "expectVerify" and "expectOpt"
- * by limiting the match mask.
- *
- * The only thing we really can't handle is incorrect byte-ordering.
- */
- const u4 matchMask = DEX_OPT_FLAG_BIG;
- u4 expectedFlags = 0;
-#if __BYTE_ORDER != __LITTLE_ENDIAN
- expectedFlags |= DEX_OPT_FLAG_BIG;
-#endif
- if (expectVerify)
- expectedFlags |= DEX_FLAG_VERIFIED;
- if (expectOpt)
- expectedFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
- if ((expectedFlags & matchMask) != (optHdr.flags & matchMask)) {
- LOGI("DexOpt: header flag mismatch (0x%02x vs 0x%02x, mask=0x%02x)\n",
- expectedFlags, optHdr.flags, matchMask);
- goto bail;
- }
-
- posn = lseek(fd, optHdr.depsOffset, SEEK_SET);
- if (posn < 0) {
- LOGW("DexOpt: seek to deps failed: %s\n", strerror(errno));
- goto bail;
- }
-
- /*
- * Read all of the dependency stuff into memory.
- */
- depData = (u1*) malloc(optHdr.depsLength);
- if (depData == NULL) {
- LOGW("DexOpt: unable to allocate %d bytes for deps\n",
- optHdr.depsLength);
- goto bail;
- }
- actual = read(fd, depData, optHdr.depsLength);
- if (actual != (ssize_t) optHdr.depsLength) {
- LOGW("DexOpt: failed reading deps: %d of %d (err=%s)\n",
- (int) actual, optHdr.depsLength, strerror(errno));
- goto bail;
- }
-
- /*
- * Verify simple items.
- */
- const u1* ptr;
- u4 val;
-
- ptr = depData;
- val = read4LE(&ptr);
- if (sourceAvail && val != modWhen) {
- LOGI("DexOpt: source file mod time mismatch (%08x vs %08x)\n",
- val, modWhen);
- goto bail;
- }
- val = read4LE(&ptr);
- if (sourceAvail && val != crc) {
- LOGI("DexOpt: source file CRC mismatch (%08x vs %08x)\n", val, crc);
- goto bail;
- }
- val = read4LE(&ptr);
- if (val != DALVIK_VM_BUILD) {
- LOGD("DexOpt: VM build version mismatch (%d vs %d)\n",
- val, DALVIK_VM_BUILD);
- goto bail;
- }
-
- /*
- * Verify dependencies on other cached DEX files. It must match
- * exactly with what is currently defined in the bootclasspath.
- */
- ClassPathEntry* cpe;
- u4 numDeps;
-
- numDeps = read4LE(&ptr);
- LOGV("+++ DexOpt: numDeps = %d\n", numDeps);
- for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
- const char* cacheFileName = getCacheFileName(cpe);
- const u1* signature = getSignature(cpe);
- size_t len = strlen(cacheFileName) +1;
- u4 storedStrLen;
-
- if (numDeps == 0) {
- /* more entries in bootclasspath than in deps list */
- LOGI("DexOpt: not all deps represented\n");
- goto bail;
- }
-
- storedStrLen = read4LE(&ptr);
- if (len != storedStrLen ||
- strcmp(cacheFileName, (const char*) ptr) != 0)
- {
- LOGI("DexOpt: mismatch dep name: '%s' vs. '%s'\n",
- cacheFileName, ptr);
- goto bail;
- }
-
- ptr += storedStrLen;
-
- if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
- LOGI("DexOpt: mismatch dep signature for '%s'\n", cacheFileName);
- goto bail;
- }
- ptr += kSHA1DigestLen;
-
- LOGV("DexOpt: dep match on '%s'\n", cacheFileName);
-
- numDeps--;
- }
-
- if (numDeps != 0) {
- /* more entries in deps list than in classpath */
- LOGI("DexOpt: Some deps went away\n");
- goto bail;
- }
-
- // consumed all data and no more?
- if (ptr != depData + optHdr.depsLength) {
- LOGW("DexOpt: Spurious dep data? %d vs %d\n",
- (int) (ptr - depData), optHdr.depsLength);
- assert(false);
- }
-
- result = true;
-
-bail:
- free(depData);
- return result;
-}
-
-/*
- * Write the dependency info to "fd" at the current file position.
- */
-static int writeDependencies(int fd, u4 modWhen, u4 crc)
-{
- u1* buf = NULL;
- ssize_t actual;
- int result = -1;
- ssize_t bufLen;
- ClassPathEntry* cpe;
- int i, numDeps;
-
- /*
- * Count up the number of completed entries in the bootclasspath.
- */
- numDeps = 0;
- bufLen = 0;
- for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
- const char* cacheFileName = getCacheFileName(cpe);
- LOGV("+++ DexOpt: found dep '%s'\n", cacheFileName);
-
- numDeps++;
- bufLen += strlen(cacheFileName) +1;
- }
-
- bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
-
- buf = malloc(bufLen);
-
- set4LE(buf+0, modWhen);
- set4LE(buf+4, crc);
- set4LE(buf+8, DALVIK_VM_BUILD);
- set4LE(buf+12, numDeps);
-
- // TODO: do we want to add dvmGetInlineOpsTableLength() here? Won't
- // help us if somebody replaces an existing entry, but it'd catch
- // additions/removals.
-
- u1* ptr = buf + 4*4;
- for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
- const char* cacheFileName = getCacheFileName(cpe);
- const u1* signature = getSignature(cpe);
- int len = strlen(cacheFileName) +1;
-
- if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
- LOGE("DexOpt: overran buffer\n");
- dvmAbort();
- }
-
- set4LE(ptr, len);
- ptr += 4;
- memcpy(ptr, cacheFileName, len);
- ptr += len;
- memcpy(ptr, signature, kSHA1DigestLen);
- ptr += kSHA1DigestLen;
- }
-
- assert(ptr == buf + bufLen);
-
- actual = write(fd, buf, bufLen);
- if (actual != bufLen) {
- result = (errno != 0) ? errno : -1;
- logFailedWrite(bufLen, actual, "dep info", errno);
- } else {
- result = 0;
- }
-
- free(buf);
- return result;
-}
-
-
-/*
- * Write a block of data in "chunk" format.
- *
- * The chunk header fields are always in "native" byte order. If "size"
- * is not a multiple of 8 bytes, the data area is padded out.
- */
-static bool writeChunk(int fd, u4 type, const void* data, size_t size)
-{
- ssize_t actual;
- union { /* save a syscall by grouping these together */
- char raw[8];
- struct {
- u4 type;
- u4 size;
- } ts;
- } header;
-
- assert(sizeof(header) == 8);
-
- LOGV("Writing chunk, type=%.4s size=%d\n", (char*) &type, size);
-
- header.ts.type = type;
- header.ts.size = (u4) size;
- actual = write(fd, &header, sizeof(header));
- if (actual != sizeof(header)) {
- logFailedWrite(size, actual, "aux chunk header write", errno);
- return false;
- }
-
- if (size > 0) {
- actual = write(fd, data, size);
- if (actual != (ssize_t) size) {
- logFailedWrite(size, actual, "aux chunk write", errno);
- return false;
- }
- }
-
- /* if necessary, pad to 64-bit alignment */
- if ((size & 7) != 0) {
- int padSize = 8 - (size & 7);
- LOGV("size was %d, inserting %d pad bytes\n", size, padSize);
- lseek(fd, padSize, SEEK_CUR);
- }
-
- assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
-
- return true;
-}
-
-/*
- * Write aux data.
- *
- * We have different pieces, some of which may be optional. To make the
- * most effective use of space, we use a "chunk" format, with a 4-byte
- * type and a 4-byte length. We guarantee 64-bit alignment for the data,
- * so it can be used directly when the file is mapped for reading.
- */
-static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,
- const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder)
-{
- /* pre-computed class lookup hash table */
- if (!writeChunk(fd, (u4) kDexChunkClassLookup,
- pClassLookup, pClassLookup->size))
- {
- return false;
- }
-
- /* remapped constants (optional) */
- if (pIndexMapSet != NULL) {
- if (!writeChunk(fd, pIndexMapSet->chunkType,
- pIndexMapSet->chunkData, pIndexMapSet->chunkDataLen))
- {
- return false;
- }
- }
-
- /* register maps (optional) */
- if (pRegMapBuilder != NULL) {
- if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
- pRegMapBuilder->data, pRegMapBuilder->size))
- {
- return false;
- }
- }
-
- /* write the end marker */
- if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
- return false;
- }
-
- return true;
-}
-
-/*
- * Log a failed write.
- */
-static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
- int err)
-{
- LOGE("Write failed: %s (%d of %d): %s\n",
- msg, (int)actual, (int)expected, strerror(err));
-}
-
-/*
- * Compute a checksum on a piece of an open file.
- *
- * File will be positioned at end of checksummed area.
- *
- * Returns "true" on success.
- */
-static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum)
-{
- unsigned char readBuf[8192];
- ssize_t actual;
- uLong adler;
-
- if (lseek(fd, start, SEEK_SET) != start) {
- LOGE("Unable to seek to start of checksum area (%ld): %s\n",
- (long) start, strerror(errno));
- return false;
- }
-
- adler = adler32(0L, Z_NULL, 0);
-
- while (length != 0) {
- size_t wanted = (length < sizeof(readBuf)) ? length : sizeof(readBuf);
- actual = read(fd, readBuf, wanted);
- if (actual <= 0) {
- LOGE("Read failed (%d) while computing checksum (len=%zu): %s\n",
- (int) actual, length, strerror(errno));
- return false;
- }
-
- adler = adler32(adler, readBuf, actual);
-
- length -= actual;
- }
-
- *pSum = adler;
- return true;
-}
-
-
-/*
- * ===========================================================================
- * Optimizations
- * ===========================================================================
- */
-
-/*
- * Perform in-place rewrites on a memory-mapped DEX file.
- *
- * This happens in a short-lived child process, so we can go nutty with
- * loading classes and allocating memory.
- */
-static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
- u4* pHeaderFlags, DexClassLookup** ppClassLookup)
-{
- u8 prepWhen, loadWhen, verifyWhen, optWhen;
- DvmDex* pDvmDex = NULL;
- bool result = false;
-
- *pHeaderFlags = 0;
-
- LOGV("+++ swapping bytes\n");
- if (dexFixByteOrdering(addr, len) != 0)
- goto bail;
-#if __BYTE_ORDER != __LITTLE_ENDIAN
- *pHeaderFlags |= DEX_OPT_FLAG_BIG;
-#endif
-
- /*
- * Now that the DEX file can be read directly, create a DexFile for it.
- */
- if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
- LOGE("Unable to create DexFile\n");
- goto bail;
- }
-
- /*
- * Create the class lookup table.
- */
- //startWhen = dvmGetRelativeTimeUsec();
- *ppClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
- if (*ppClassLookup == NULL)
- goto bail;
-
- /*
- * Bail out early if they don't want The Works. The current implementation
- * doesn't fork a new process if this flag isn't set, so we really don't
- * want to continue on with the crazy class loading.
- */
- if (!doVerify && !doOpt) {
- result = true;
- goto bail;
- }
-
- /* this is needed for the next part */
- pDvmDex->pDexFile->pClassLookup = *ppClassLookup;
-
- prepWhen = dvmGetRelativeTimeUsec();
-
- /*
- * Load all classes found in this DEX file. If they fail to load for
- * some reason, they won't get verified (which is as it should be).
- */
- if (!loadAllClasses(pDvmDex))
- goto bail;
- loadWhen = dvmGetRelativeTimeUsec();
-
- /*
- * Verify all classes in the DEX file. Export the "is verified" flag
- * to the DEX file we're creating.
- */
- if (doVerify) {
- dvmVerifyAllClasses(pDvmDex->pDexFile);
- *pHeaderFlags |= DEX_FLAG_VERIFIED;
- }
- verifyWhen = dvmGetRelativeTimeUsec();
-
- /*
- * Optimize the classes we successfully loaded. If the opt mode is
- * OPTIMIZE_MODE_VERIFIED, each class must have been successfully
- * verified or we'll skip it.
- */
-#ifndef PROFILE_FIELD_ACCESS
- if (doOpt) {
- optimizeLoadedClasses(pDvmDex->pDexFile);
- *pHeaderFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
- }
-#endif
- optWhen = dvmGetRelativeTimeUsec();
-
- LOGD("DexOpt: load %dms, verify %dms, opt %dms\n",
- (int) (loadWhen - prepWhen) / 1000,
- (int) (verifyWhen - loadWhen) / 1000,
- (int) (optWhen - verifyWhen) / 1000);
-
- result = true;
-
-bail:
- /* free up storage */
- dvmDexFileFree(pDvmDex);
-
- return result;
-}
-
-/*
- * Update the Adler-32 checksum stored in the DEX file. This covers the
- * swapped and optimized DEX data, but does not include the opt header
- * or auxillary data.
- */
-static void updateChecksum(u1* addr, int len, DexHeader* pHeader)
-{
- /*
- * Rewrite the checksum. We leave the SHA-1 signature alone.
- */
- uLong adler = adler32(0L, Z_NULL, 0);
- const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
-
- adler = adler32(adler, addr + nonSum, len - nonSum);
- pHeader->checksum = adler;
-}
-
-/*
- * Try to load all classes in the specified DEX. If they have some sort
- * of broken dependency, e.g. their superclass lives in a different DEX
- * that wasn't previously loaded into the bootstrap class path, loading
- * will fail. This is the desired behavior.
- *
- * We have no notion of class loader at this point, so we load all of
- * the classes with the bootstrap class loader. It turns out this has
- * exactly the behavior we want, and has no ill side effects because we're
- * running in a separate process and anything we load here will be forgotten.
- *
- * We set the CLASS_MULTIPLE_DEFS flag here if we see multiple definitions.
- * This works because we only call here as part of optimization / pre-verify,
- * not during verification as part of loading a class into a running VM.
- *
- * This returns "false" if the world is too screwed up to do anything
- * useful at all.
- */
-static bool loadAllClasses(DvmDex* pDvmDex)
-{
- u4 count = pDvmDex->pDexFile->pHeader->classDefsSize;
- u4 idx;
- int loaded = 0;
-
- LOGV("DexOpt: +++ trying to load %d classes\n", count);
-
- dvmSetBootPathExtraDex(pDvmDex);
-
- /*
- * We have some circularity issues with Class and Object that are most
- * easily avoided by ensuring that Object is never the first thing we
- * try to find. Take care of that here. (We only need to do this when
- * loading classes from the DEX file that contains Object, and only
- * when Object comes first in the list, but it costs very little to
- * do it in all cases.)
- */
- if (dvmFindSystemClass("Ljava/lang/Class;") == NULL) {
- LOGE("ERROR: java.lang.Class does not exist!\n");
- return false;
- }
-
- for (idx = 0; idx < count; idx++) {
- const DexClassDef* pClassDef;
- const char* classDescriptor;
- ClassObject* newClass;
-
- pClassDef = dexGetClassDef(pDvmDex->pDexFile, idx);
- classDescriptor =
- dexStringByTypeIdx(pDvmDex->pDexFile, pClassDef->classIdx);
-
- LOGV("+++ loading '%s'", classDescriptor);
- //newClass = dvmDefineClass(pDexFile, classDescriptor,
- // NULL);
- newClass = dvmFindSystemClassNoInit(classDescriptor);
- if (newClass == NULL) {
- LOGV("DexOpt: failed loading '%s'\n", classDescriptor);
- dvmClearOptException(dvmThreadSelf());
- } else if (newClass->pDvmDex != pDvmDex) {
- /*
- * We don't load the new one, and we tag the first one found
- * with the "multiple def" flag so the resolver doesn't try
- * to make it available.
- */
- LOGD("DexOpt: '%s' has an earlier definition; blocking out\n",
- classDescriptor);
- SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
- } else {
- loaded++;
- }
- }
- LOGV("DexOpt: +++ successfully loaded %d classes\n", loaded);
-
- dvmSetBootPathExtraDex(NULL);
- return true;
-}
-
-
-/*
- * Create a table of inline substitutions.
- *
- * TODO: this is currently just a linear array. We will want to put this
- * into a hash table as the list size increases.
- */
-static InlineSub* createInlineSubsTable(void)
-{
- const InlineOperation* ops = dvmGetInlineOpsTable();
- const int count = dvmGetInlineOpsTableLength();
- InlineSub* table;
- Method* method;
- ClassObject* clazz;
- int i, tableIndex;
-
- /*
- * Allocate for optimism: one slot per entry, plus an end-of-list marker.
- */
- table = malloc(sizeof(InlineSub) * (count+1));
-
- tableIndex = 0;
- for (i = 0; i < count; i++) {
- clazz = dvmFindClassNoInit(ops[i].classDescriptor, NULL);
- if (clazz == NULL) {
- LOGV("DexOpt: can't inline for class '%s': not found\n",
- ops[i].classDescriptor);
- dvmClearOptException(dvmThreadSelf());
- } else {
- /*
- * Method could be virtual or direct. Try both. Don't use
- * the "hier" versions.
- */
- method = dvmFindDirectMethodByDescriptor(clazz, ops[i].methodName,
- ops[i].methodSignature);
- if (method == NULL)
- method = dvmFindVirtualMethodByDescriptor(clazz, ops[i].methodName,
- ops[i].methodSignature);
- if (method == NULL) {
- LOGW("DexOpt: can't inline %s.%s %s: method not found\n",
- ops[i].classDescriptor, ops[i].methodName,
- ops[i].methodSignature);
- } else {
- if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
- LOGW("DexOpt: WARNING: inline op on non-final class/method "
- "%s.%s\n",
- clazz->descriptor, method->name);
- /* fail? */
- }
- if (dvmIsSynchronizedMethod(method) ||
- dvmIsDeclaredSynchronizedMethod(method))
- {
- LOGW("DexOpt: WARNING: inline op on synchronized method "
- "%s.%s\n",
- clazz->descriptor, method->name);
- /* fail? */
- }
-
- table[tableIndex].method = method;
- table[tableIndex].inlineIdx = i;
- tableIndex++;
-
- LOGV("DexOpt: will inline %d: %s.%s %s\n", i,
- ops[i].classDescriptor, ops[i].methodName,
- ops[i].methodSignature);
- }
- }
- }
-
- /* mark end of table */
- table[tableIndex].method = NULL;
- LOGV("DexOpt: inline table has %d entries\n", tableIndex);
-
- return table;
-}
-
-/*
- * Run through all classes that were successfully loaded from this DEX
- * file and optimize their code sections.
- */
-static void optimizeLoadedClasses(DexFile* pDexFile)
-{
- u4 count = pDexFile->pHeader->classDefsSize;
- u4 idx;
- InlineSub* inlineSubs = NULL;
-
- LOGV("DexOpt: +++ optimizing up to %d classes\n", count);
- assert(gDvm.dexOptMode != OPTIMIZE_MODE_NONE);
-
- inlineSubs = createInlineSubsTable();
-
- for (idx = 0; idx < count; idx++) {
- const DexClassDef* pClassDef;
- const char* classDescriptor;
- ClassObject* clazz;
-
- pClassDef = dexGetClassDef(pDexFile, idx);
- classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
-
- /* all classes are loaded into the bootstrap class loader */
- clazz = dvmLookupClass(classDescriptor, NULL, false);
- if (clazz != NULL) {
- if ((pClassDef->accessFlags & CLASS_ISPREVERIFIED) == 0 &&
- gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
- {
- LOGV("DexOpt: not optimizing '%s': not verified\n",
- classDescriptor);
- } else if (clazz->pDvmDex->pDexFile != pDexFile) {
- /* shouldn't be here -- verifier should have caught */
- LOGD("DexOpt: not optimizing '%s': multiple definitions\n",
- classDescriptor);
- } else {
- optimizeClass(clazz, inlineSubs);
-
- /* set the flag whether or not we actually did anything */
- ((DexClassDef*)pClassDef)->accessFlags |=
- CLASS_ISOPTIMIZED;
- }
- } else {
- LOGV("DexOpt: not optimizing unavailable class '%s'\n",
- classDescriptor);
- }
- }
-
- free(inlineSubs);
-}
-
-/*
- * Optimize the specified class.
- */
-static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs)
-{
- int i;
-
- for (i = 0; i < clazz->directMethodCount; i++) {
- if (!optimizeMethod(&clazz->directMethods[i], inlineSubs))
- goto fail;
- }
- for (i = 0; i < clazz->virtualMethodCount; i++) {
- if (!optimizeMethod(&clazz->virtualMethods[i], inlineSubs))
- goto fail;
- }
-
- return;
-
-fail:
- LOGV("DexOpt: ceasing optimization attempts on %s\n", clazz->descriptor);
-}
-
-/*
- * Optimize instructions in a method.
- *
- * Returns "true" if all went well, "false" if we bailed out early when
- * something failed.
- */
-static bool optimizeMethod(Method* method, const InlineSub* inlineSubs)
-{
- u4 insnsSize;
- u2* insns;
- u2 inst;
-
- if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
- return true;
-
- insns = (u2*) method->insns;
- assert(insns != NULL);
- insnsSize = dvmGetMethodInsnsSize(method);
-
- while (insnsSize > 0) {
- int width;
-
- inst = *insns & 0xff;
-
- switch (inst) {
- case OP_IGET:
- case OP_IGET_BOOLEAN:
- case OP_IGET_BYTE:
- case OP_IGET_CHAR:
- case OP_IGET_SHORT:
- rewriteInstField(method, insns, OP_IGET_QUICK);
- break;
- case OP_IGET_WIDE:
- rewriteInstField(method, insns, OP_IGET_WIDE_QUICK);
- break;
- case OP_IGET_OBJECT:
- rewriteInstField(method, insns, OP_IGET_OBJECT_QUICK);
- break;
- case OP_IPUT:
- case OP_IPUT_BOOLEAN:
- case OP_IPUT_BYTE:
- case OP_IPUT_CHAR:
- case OP_IPUT_SHORT:
- rewriteInstField(method, insns, OP_IPUT_QUICK);
- break;
- case OP_IPUT_WIDE:
- rewriteInstField(method, insns, OP_IPUT_WIDE_QUICK);
- break;
- case OP_IPUT_OBJECT:
- rewriteInstField(method, insns, OP_IPUT_OBJECT_QUICK);
- break;
-
- case OP_INVOKE_VIRTUAL:
- if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL,inlineSubs))
- {
- if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_VIRTUAL_QUICK))
- return false;
- }
- break;
- case OP_INVOKE_VIRTUAL_RANGE:
- if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL,
- inlineSubs))
- {
- if (!rewriteVirtualInvoke(method, insns,
- OP_INVOKE_VIRTUAL_QUICK_RANGE))
- {
- return false;
- }
- }
- break;
- case OP_INVOKE_SUPER:
- if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK))
- return false;
- break;
- case OP_INVOKE_SUPER_RANGE:
- if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE))
- return false;
- break;
-
- case OP_INVOKE_DIRECT:
- if (!rewriteExecuteInline(method, insns, METHOD_DIRECT, inlineSubs))
- {
- if (!rewriteEmptyDirectInvoke(method, insns))
- return false;
- }
- break;
- case OP_INVOKE_DIRECT_RANGE:
- rewriteExecuteInlineRange(method, insns, METHOD_DIRECT, inlineSubs);
- break;
-
- case OP_INVOKE_STATIC:
- rewriteExecuteInline(method, insns, METHOD_STATIC, inlineSubs);
- break;
- case OP_INVOKE_STATIC_RANGE:
- rewriteExecuteInlineRange(method, insns, METHOD_STATIC, inlineSubs);
- break;
-
- default:
- // ignore this instruction
- ;
- }
-
- if (*insns == kPackedSwitchSignature) {
- width = 4 + insns[1] * 2;
- } else if (*insns == kSparseSwitchSignature) {
- width = 2 + insns[1] * 4;
- } else if (*insns == kArrayDataSignature) {
- u2 elemWidth = insns[1];
- u4 len = insns[2] | (((u4)insns[3]) << 16);
- width = 4 + (elemWidth * len + 1) / 2;
- } else {
- width = dexGetInstrWidthAbs(gDvm.instrWidth, inst);
- }
- assert(width > 0);
-
- insns += width;
- insnsSize -= width;
- }
-
- assert(insnsSize == 0);
- return true;
-}
-
-
-/*
- * If "referrer" and "resClass" don't come from the same DEX file, and
- * the DEX we're working on is not destined for the bootstrap class path,
- * tweak the class loader so package-access checks work correctly.
- *
- * Only do this if we're doing pre-verification or optimization.
- */
-static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
-{
- if (!gDvm.optimizing)
- return;
- assert(referrer->classLoader == NULL);
- assert(resClass->classLoader == NULL);
-
- if (!gDvm.optimizingBootstrapClass) {
- /* class loader for an array class comes from element type */
- if (dvmIsArrayClass(resClass))
- resClass = resClass->elementClass;
- if (referrer->pDvmDex != resClass->pDvmDex)
- resClass->classLoader = (Object*) 0xdead3333;
- }
-}
-
-/*
- * Undo the effects of tweakLoader.
- */
-static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
-{
- if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
- return;
-
- if (dvmIsArrayClass(resClass))
- resClass = resClass->elementClass;
- resClass->classLoader = NULL;
-}
-
-
-/*
- * Alternate version of dvmResolveClass for use with verification and
- * optimization. Performs access checks on every resolve, and refuses
- * to acknowledge the existence of classes defined in more than one DEX
- * file.
- *
- * Exceptions caused by failures are cleared before returning.
- *
- * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
- */
-ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
- VerifyError* pFailure)
-{
- DvmDex* pDvmDex = referrer->pDvmDex;
- ClassObject* resClass;
-
- /*
- * Check the table first. If not there, do the lookup by name.
- */
- resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
- if (resClass == NULL) {
- const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
- if (className[0] != '\0' && className[1] == '\0') {
- /* primitive type */
- resClass = dvmFindPrimitiveClass(className[0]);
- } else {
- resClass = dvmFindClassNoInit(className, referrer->classLoader);
- }
- if (resClass == NULL) {
- /* not found, exception should be raised */
- LOGV("DexOpt: class %d (%s) not found\n",
- classIdx,
- dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
- if (pFailure != NULL) {
- /* dig through the wrappers to find the original failure */
- Object* excep = dvmGetException(dvmThreadSelf());
- while (true) {
- Object* cause = dvmGetExceptionCause(excep);
- if (cause == NULL)
- break;
- excep = cause;
- }
- if (strcmp(excep->clazz->descriptor,
- "Ljava/lang/IncompatibleClassChangeError;") == 0)
- {
- *pFailure = VERIFY_ERROR_CLASS_CHANGE;
- } else {
- *pFailure = VERIFY_ERROR_NO_CLASS;
- }
- }
- dvmClearOptException(dvmThreadSelf());
- return NULL;
- }
-
- /*
- * Add it to the resolved table so we're faster on the next lookup.
- */
- dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
- }
-
- /* multiple definitions? */
- if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
- LOGI("DexOpt: not resolving ambiguous class '%s'\n",
- resClass->descriptor);
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_NO_CLASS;
- return NULL;
- }
-
- /* access allowed? */
- tweakLoader(referrer, resClass);
- bool allowed = dvmCheckClassAccess(referrer, resClass);
- untweakLoader(referrer, resClass);
- if (!allowed) {
- LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
- referrer->descriptor, resClass->descriptor);
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_ACCESS_CLASS;
- return NULL;
- }
-
- return resClass;
-}
-
-/*
- * Alternate version of dvmResolveInstField().
- *
- * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
- */
-InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
- VerifyError* pFailure)
-{
- DvmDex* pDvmDex = referrer->pDvmDex;
- InstField* resField;
-
- resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
- if (resField == NULL) {
- const DexFieldId* pFieldId;
- ClassObject* resClass;
-
- pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
-
- /*
- * Find the field's class.
- */
- resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
- if (resClass == NULL) {
- //dvmClearOptException(dvmThreadSelf());
- assert(!dvmCheckException(dvmThreadSelf()));
- if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
- return NULL;
- }
-
- resField = (InstField*)dvmFindFieldHier(resClass,
- dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
- dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
- if (resField == NULL) {
- LOGD("DexOpt: couldn't find field %s.%s\n",
- resClass->descriptor,
- dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_NO_FIELD;
- return NULL;
- }
- if (dvmIsStaticField(&resField->field)) {
- LOGD("DexOpt: wanted instance, got static for field %s.%s\n",
- resClass->descriptor,
- dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_CLASS_CHANGE;
- return NULL;
- }
-
- /*
- * Add it to the resolved table so we're faster on the next lookup.
- */
- dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
- }
-
- /* access allowed? */
- tweakLoader(referrer, resField->field.clazz);
- bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
- untweakLoader(referrer, resField->field.clazz);
- if (!allowed) {
- LOGI("DexOpt: access denied from %s to field %s.%s\n",
- referrer->descriptor, resField->field.clazz->descriptor,
- resField->field.name);
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_ACCESS_FIELD;
- return NULL;
- }
-
- return resField;
-}
-
-/*
- * Alternate version of dvmResolveStaticField().
- *
- * Does not force initialization of the resolved field's class.
- *
- * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
- */
-StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
- VerifyError* pFailure)
-{
- DvmDex* pDvmDex = referrer->pDvmDex;
- StaticField* resField;
-
- resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
- if (resField == NULL) {
- const DexFieldId* pFieldId;
- ClassObject* resClass;
-
- pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
-
- /*
- * Find the field's class.
- */
- resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
- if (resClass == NULL) {
- //dvmClearOptException(dvmThreadSelf());
- assert(!dvmCheckException(dvmThreadSelf()));
- if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
- return NULL;
- }
-
- resField = (StaticField*)dvmFindFieldHier(resClass,
- dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
- dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
- if (resField == NULL) {
- LOGD("DexOpt: couldn't find static field\n");
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_NO_FIELD;
- return NULL;
- }
- if (!dvmIsStaticField(&resField->field)) {
- LOGD("DexOpt: wanted static, got instance for field %s.%s\n",
- resClass->descriptor,
- dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_CLASS_CHANGE;
- return NULL;
- }
-
- /*
- * Add it to the resolved table so we're faster on the next lookup.
- *
- * We can only do this if we're in "dexopt", because the presence
- * of a valid value in the resolution table implies that the class
- * containing the static field has been initialized.
- */
- if (gDvm.optimizing)
- dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
- }
-
- /* access allowed? */
- tweakLoader(referrer, resField->field.clazz);
- bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
- untweakLoader(referrer, resField->field.clazz);
- if (!allowed) {
- LOGI("DexOpt: access denied from %s to field %s.%s\n",
- referrer->descriptor, resField->field.clazz->descriptor,
- resField->field.name);
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_ACCESS_FIELD;
- return NULL;
- }
-
- return resField;
-}
-
-
-/*
- * Rewrite an iget/iput instruction. These all have the form:
- * op vA, vB, field@CCCC
- *
- * Where vA holds the value, vB holds the object reference, and CCCC is
- * the field reference constant pool offset. We want to replace CCCC
- * with the byte offset from the start of the object.
- *
- * "clazz" is the referring class. We need this because we verify
- * access rights here.
- */
-static void rewriteInstField(Method* method, u2* insns, OpCode newOpc)
-{
- ClassObject* clazz = method->clazz;
- u2 fieldIdx = insns[1];
- InstField* field;
- int byteOffset;
-
- field = dvmOptResolveInstField(clazz, fieldIdx, NULL);
- if (field == NULL) {
- LOGI("DexOpt: unable to optimize field ref 0x%04x at 0x%02x in %s.%s\n",
- fieldIdx, (int) (insns - method->insns), clazz->descriptor,
- method->name);
- return;
- }
-
- if (field->byteOffset >= 65536) {
- LOGI("DexOpt: field offset exceeds 64K (%d)\n", field->byteOffset);
- return;
- }
-
- insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
- insns[1] = (u2) field->byteOffset;
- LOGVV("DexOpt: rewrote access to %s.%s --> %d\n",
- field->field.clazz->descriptor, field->field.name,
- field->byteOffset);
-}
-
-/*
- * Alternate version of dvmResolveMethod().
- *
- * Doesn't throw exceptions, and checks access on every lookup.
- *
- * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
- */
-Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
- MethodType methodType, VerifyError* pFailure)
-{
- DvmDex* pDvmDex = referrer->pDvmDex;
- Method* resMethod;
-
- assert(methodType == METHOD_DIRECT ||
- methodType == METHOD_VIRTUAL ||
- methodType == METHOD_STATIC);
-
- LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx,
- referrer->descriptor);
-
- resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
- if (resMethod == NULL) {
- const DexMethodId* pMethodId;
- ClassObject* resClass;
-
- pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
-
- resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
- if (resClass == NULL) {
- /*
- * Can't find the class that the method is a part of, or don't
- * have permission to access the class.
- */
- LOGV("DexOpt: can't find called method's class (?.%s)\n",
- dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
- if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
- return NULL;
- }
- if (dvmIsInterfaceClass(resClass)) {
- /* method is part of an interface; this is wrong method for that */
- LOGW("DexOpt: method is in an interface\n");
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_GENERIC;
- return NULL;
- }
-
- /*
- * We need to chase up the class hierarchy to find methods defined
- * in super-classes. (We only want to check the current class
- * if we're looking for a constructor.)
- */
- DexProto proto;
- dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
-
- if (methodType == METHOD_DIRECT) {
- resMethod = dvmFindDirectMethod(resClass,
- dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
- } else {
- /* METHOD_STATIC or METHOD_VIRTUAL */
- resMethod = dvmFindMethodHier(resClass,
- dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
- }
-
- if (resMethod == NULL) {
- LOGV("DexOpt: couldn't find method '%s'\n",
- dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_NO_METHOD;
- return NULL;
- }
- if (methodType == METHOD_STATIC) {
- if (!dvmIsStaticMethod(resMethod)) {
- LOGD("DexOpt: wanted static, got instance for method %s.%s\n",
- resClass->descriptor, resMethod->name);
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_CLASS_CHANGE;
- return NULL;
- }
- } else if (methodType == METHOD_VIRTUAL) {
- if (dvmIsStaticMethod(resMethod)) {
- LOGD("DexOpt: wanted instance, got static for method %s.%s\n",
- resClass->descriptor, resMethod->name);
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_CLASS_CHANGE;
- return NULL;
- }
- }
-
- /* see if this is a pure-abstract method */
- if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
- LOGW("DexOpt: pure-abstract method '%s' in %s\n",
- dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
- resClass->descriptor);
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_GENERIC;
- return NULL;
- }
-
- /*
- * Add it to the resolved table so we're faster on the next lookup.
- *
- * We can only do this for static methods if we're not in "dexopt",
- * because the presence of a valid value in the resolution table
- * implies that the class containing the static field has been
- * initialized.
- */
- if (methodType != METHOD_STATIC || gDvm.optimizing)
- dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
- }
-
- LOGVV("--- found method %d (%s.%s)\n",
- methodIdx, resMethod->clazz->descriptor, resMethod->name);
-
- /* access allowed? */
- tweakLoader(referrer, resMethod->clazz);
- bool allowed = dvmCheckMethodAccess(referrer, resMethod);
- untweakLoader(referrer, resMethod->clazz);
- if (!allowed) {
- IF_LOGI() {
- char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
- LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
- resMethod->clazz->descriptor, resMethod->name, desc,
- referrer->descriptor);
- free(desc);
- }
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_ACCESS_METHOD;
- return NULL;
- }
-
- return resMethod;
-}
-
-/*
- * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
- * invoke-super/range. These all have the form:
- * op vAA, meth@BBBB, reg stuff @CCCC
- *
- * We want to replace the method constant pool index BBBB with the
- * vtable index.
- */
-static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc)
-{
- ClassObject* clazz = method->clazz;
- Method* baseMethod;
- u2 methodIdx = insns[1];
-
- baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
- if (baseMethod == NULL) {
- LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n",
- methodIdx,
- (int) (insns - method->insns), clazz->descriptor,
- method->name);
- return false;
- }
-
- assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
- (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
- (insns[0] & 0xff) == OP_INVOKE_SUPER ||
- (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
-
- /*
- * Note: Method->methodIndex is a u2 and is range checked during the
- * initial load.
- */
- insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
- insns[1] = baseMethod->methodIndex;
-
- //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
- // method->clazz->descriptor, method->name,
- // baseMethod->clazz->descriptor, baseMethod->name);
-
- return true;
-}
-
-/*
- * Rewrite invoke-direct, which has the form:
- * op vAA, meth@BBBB, reg stuff @CCCC
- *
- * There isn't a lot we can do to make this faster, but in some situations
- * we can make it go away entirely.
- *
- * This must only be used when the invoked method does nothing and has
- * no return value (the latter being very important for verification).
- */
-static bool rewriteEmptyDirectInvoke(Method* method, u2* insns)
-{
- ClassObject* clazz = method->clazz;
- Method* calledMethod;
- u2 methodIdx = insns[1];
-
- calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
- if (calledMethod == NULL) {
- LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n",
- methodIdx,
- (int) (insns - method->insns), clazz->descriptor,
- method->name);
- return false;
- }
-
- /* TODO: verify that java.lang.Object() is actually empty! */
- if (calledMethod->clazz == gDvm.classJavaLangObject &&
- dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
- {
- /*
- * Replace with "empty" instruction. DO NOT disturb anything
- * else about it, as we want it to function the same as
- * OP_INVOKE_DIRECT when debugging is enabled.
- */
- assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
- insns[0] = (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY;
-
- //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
- // method->clazz->descriptor, method->name,
- // calledMethod->clazz->descriptor, calledMethod->name);
- }
-
- return true;
-}
-
-/*
- * Resolve an interface method reference.
- *
- * No method access check here -- interface methods are always public.
- *
- * Returns NULL if the method was not found. Does not throw an exception.
- */
-Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
-{
- DvmDex* pDvmDex = referrer->pDvmDex;
- Method* resMethod;
- int i;
-
- LOGVV("--- resolving interface method %d (referrer=%s)\n",
- methodIdx, referrer->descriptor);
-
- resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
- if (resMethod == NULL) {
- const DexMethodId* pMethodId;
- ClassObject* resClass;
-
- pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
-
- resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
- if (resClass == NULL) {
- /* can't find the class that the method is a part of */
- dvmClearOptException(dvmThreadSelf());
- return NULL;
- }
- if (!dvmIsInterfaceClass(resClass)) {
- /* whoops */
- LOGI("Interface method not part of interface class\n");
- return NULL;
- }
-
- const char* methodName =
- dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
- DexProto proto;
- dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
-
- LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
- methodName, methodSig, resClass->descriptor);
- resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
- if (resMethod == NULL) {
- /* scan superinterfaces and superclass interfaces */
- LOGVV("+++ did not resolve immediately\n");
- for (i = 0; i < resClass->iftableCount; i++) {
- resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
- methodName, &proto);
- if (resMethod != NULL)
- break;
- }
-
- if (resMethod == NULL) {
- LOGVV("+++ unable to resolve method %s\n", methodName);
- return NULL;
- }
- } else {
- LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
- resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
- }
-
- /* we're expecting this to be abstract */
- if (!dvmIsAbstractMethod(resMethod)) {
- char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
- LOGW("Found non-abstract interface method %s.%s %s\n",
- resMethod->clazz->descriptor, resMethod->name, desc);
- free(desc);
- return NULL;
- }
-
- /*
- * Add it to the resolved table so we're faster on the next lookup.
- */
- dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
- }
-
- LOGVV("--- found interface method %d (%s.%s)\n",
- methodIdx, resMethod->clazz->descriptor, resMethod->name);
-
- /* interface methods are always public; no need to check access */
-
- return resMethod;
-}
-
-/*
- * See if the method being called can be rewritten as an inline operation.
- * Works for invoke-virtual, invoke-direct, and invoke-static.
- *
- * Returns "true" if we replace it.
- */
-static bool rewriteExecuteInline(Method* method, u2* insns,
- MethodType methodType, const InlineSub* inlineSubs)
-{
- ClassObject* clazz = method->clazz;
- Method* calledMethod;
- u2 methodIdx = insns[1];
-
- //return false;
-
- calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
- if (calledMethod == NULL) {
- LOGV("+++ DexOpt inline: can't find %d\n", methodIdx);
- return false;
- }
-
- while (inlineSubs->method != NULL) {
- /*
- if (extra) {
- LOGI("comparing %p vs %p %s.%s %s\n",
- inlineSubs->method, calledMethod,
- inlineSubs->method->clazz->descriptor,
- inlineSubs->method->name,
- inlineSubs->method->signature);
- }
- */
- if (inlineSubs->method == calledMethod) {
- assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
- (insns[0] & 0xff) == OP_INVOKE_STATIC ||
- (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
- insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE;
- insns[1] = (u2) inlineSubs->inlineIdx;
-
- //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
- // method->clazz->descriptor, method->name,
- // calledMethod->clazz->descriptor, calledMethod->name);
- return true;
- }
-
- inlineSubs++;
- }
-
- return false;
-}
-
-/*
- * See if the method being called can be rewritten as an inline operation.
- * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range.
- *
- * Returns "true" if we replace it.
- */
-static bool rewriteExecuteInlineRange(Method* method, u2* insns,
- MethodType methodType, const InlineSub* inlineSubs)
-{
- ClassObject* clazz = method->clazz;
- Method* calledMethod;
- u2 methodIdx = insns[1];
-
- calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
- if (calledMethod == NULL) {
- LOGV("+++ DexOpt inline/range: can't find %d\n", methodIdx);
- return false;
- }
-
- while (inlineSubs->method != NULL) {
- if (inlineSubs->method == calledMethod) {
- assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
- (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
- (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
- insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE;
- insns[1] = (u2) inlineSubs->inlineIdx;
-
- //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
- // method->clazz->descriptor, method->name,
- // calledMethod->clazz->descriptor, calledMethod->name);
- return true;
- }
-
- inlineSubs++;
- }
-
- return false;
-}
-
diff --git a/vm/analysis/DexPrepare.c b/vm/analysis/DexPrepare.c
new file mode 100644
index 0000000..eea25c8
--- /dev/null
+++ b/vm/analysis/DexPrepare.c
@@ -0,0 +1,1207 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Prepare a DEX file for use by the VM. Depending upon the VM options
+ * we will attempt to verify and/or optimize the code, possibly appending
+ * register maps.
+ *
+ * TODO: the format of the optimized header is currently "whatever we
+ * happen to write", since the VM that writes it is by definition the same
+ * as the VM that reads it. Still, it should be better documented and
+ * more rigorously structured.
+ */
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+#include "analysis/RegisterMap.h"
+#include "analysis/Optimize.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+/* fwd */
+static void updateChecksum(u1* addr, int len, DexHeader* pHeader);
+static int writeDependencies(int fd, u4 modWhen, u4 crc);
+static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,\
+ const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder);
+static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
+ int err);
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum);
+
+
+/*
+ * Return the fd of an open file in the DEX file cache area. If the cache
+ * file doesn't exist or is out of date, this will remove the old entry,
+ * create a new one (writing only the file header), and return with the
+ * "new file" flag set.
+ *
+ * It's possible to execute from an unoptimized DEX file directly,
+ * assuming the byte ordering and structure alignment is correct, but
+ * disadvantageous because some significant optimizations are not possible.
+ * It's not generally possible to do the same from an uncompressed Jar
+ * file entry, because we have to guarantee 32-bit alignment in the
+ * memory-mapped file.
+ *
+ * For a Jar/APK file (a zip archive with "classes.dex" inside), "modWhen"
+ * and "crc32" come from the Zip directory entry. For a stand-alone DEX
+ * file, it's the modification date of the file and the Adler32 from the
+ * DEX header (which immediately follows the magic). If these don't
+ * match what's stored in the opt header, we reject the file immediately.
+ *
+ * On success, the file descriptor will be positioned just past the "opt"
+ * file header, and will be locked with flock. "*pCachedName" will point
+ * to newly-allocated storage.
+ */
+int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
+ u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
+{
+ int fd, cc;
+ struct stat fdStat, fileStat;
+ bool readOnly = false;
+
+ *pNewFile = false;
+
+retry:
+ /*
+ * Try to open the cache file. If we've been asked to,
+ * create it if it doesn't exist.
+ */
+ fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
+ if (fd < 0) {
+ fd = open(cacheFileName, O_RDONLY, 0);
+ if (fd < 0) {
+ if (createIfMissing) {
+ LOGE("Can't open dex cache '%s': %s\n",
+ cacheFileName, strerror(errno));
+ }
+ return fd;
+ }
+ readOnly = true;
+ }
+
+ /*
+ * Grab an exclusive lock on the cache file. If somebody else is
+ * working on it, we'll block here until they complete. Because
+ * we're waiting on an external resource, we go into VMWAIT mode.
+ */
+ int oldStatus;
+ LOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)\n",
+ cacheFileName, fd, isBootstrap);
+ oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+ cc = flock(fd, LOCK_EX | LOCK_NB);
+ if (cc != 0) {
+ LOGD("DexOpt: sleeping on flock(%s)\n", cacheFileName);
+ cc = flock(fd, LOCK_EX);
+ }
+ dvmChangeStatus(NULL, oldStatus);
+ if (cc != 0) {
+ LOGE("Can't lock dex cache '%s': %d\n", cacheFileName, cc);
+ close(fd);
+ return -1;
+ }
+ LOGV("DexOpt: locked cache file\n");
+
+ /*
+ * Check to see if the fd we opened and locked matches the file in
+ * the filesystem. If they don't, then somebody else unlinked ours
+ * and created a new file, and we need to use that one instead. (If
+ * we caught them between the unlink and the create, we'll get an
+ * ENOENT from the file stat.)
+ */
+ cc = fstat(fd, &fdStat);
+ if (cc != 0) {
+ LOGE("Can't stat open file '%s'\n", cacheFileName);
+ LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
+ goto close_fail;
+ }
+ cc = stat(cacheFileName, &fileStat);
+ if (cc != 0 ||
+ fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
+ {
+ LOGD("DexOpt: our open cache file is stale; sleeping and retrying\n");
+ LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
+ flock(fd, LOCK_UN);
+ close(fd);
+ usleep(250 * 1000); /* if something is hosed, don't peg machine */
+ goto retry;
+ }
+
+ /*
+ * We have the correct file open and locked. If the file size is zero,
+ * then it was just created by us, and we want to fill in some fields
+ * in the "opt" header and set "*pNewFile". Otherwise, we want to
+ * verify that the fields in the header match our expectations, and
+ * reset the file if they don't.
+ */
+ if (fdStat.st_size == 0) {
+ if (readOnly) {
+ LOGW("DexOpt: file has zero length and isn't writable\n");
+ goto close_fail;
+ }
+ cc = dexOptCreateEmptyHeader(fd);
+ if (cc != 0)
+ goto close_fail;
+ *pNewFile = true;
+ LOGV("DexOpt: successfully initialized new cache file\n");
+ } else {
+ bool expectVerify, expectOpt;
+
+ if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
+ expectVerify = false;
+ else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
+ expectVerify = !isBootstrap;
+ else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
+ expectVerify = true;
+
+ if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
+ expectOpt = false;
+ else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
+ expectOpt = expectVerify;
+ else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
+ expectOpt = true;
+
+ LOGV("checking deps, expecting vfy=%d opt=%d\n",
+ expectVerify, expectOpt);
+
+ if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
+ expectVerify, expectOpt))
+ {
+ if (readOnly) {
+ /*
+ * We could unlink and rewrite the file if we own it or
+ * the "sticky" bit isn't set on the directory. However,
+ * we're not able to truncate it, which spoils things. So,
+ * give up now.
+ */
+ if (createIfMissing) {
+ LOGW("Cached DEX '%s' (%s) is stale and not writable\n",
+ fileName, cacheFileName);
+ }
+ goto close_fail;
+ }
+
+ /*
+ * If we truncate the existing file before unlinking it, any
+ * process that has it mapped will fail when it tries to touch
+ * the pages.
+ *
+ * This is very important. The zygote process will have the
+ * boot DEX files (core, framework, etc.) mapped early. If
+ * (say) core.dex gets updated, and somebody launches an app
+ * that uses App.dex, then App.dex gets reoptimized because it's
+ * dependent upon the boot classes. However, dexopt will be
+ * using the *new* core.dex to do the optimizations, while the
+ * app will actually be running against the *old* core.dex
+ * because it starts from zygote.
+ *
+ * Even without zygote, it's still possible for a class loader
+ * to pull in an APK that was optimized against an older set
+ * of DEX files. We must ensure that everything fails when a
+ * boot DEX gets updated, and for general "why aren't my
+ * changes doing anything" purposes its best if we just make
+ * everything crash when a DEX they're using gets updated.
+ */
+ LOGD("Stale deps in cache file; removing and retrying\n");
+ if (ftruncate(fd, 0) != 0) {
+ LOGW("Warning: unable to truncate cache file '%s': %s\n",
+ cacheFileName, strerror(errno));
+ /* keep going */
+ }
+ if (unlink(cacheFileName) != 0) {
+ LOGW("Warning: unable to remove cache file '%s': %d %s\n",
+ cacheFileName, errno, strerror(errno));
+ /* keep going; permission failure should probably be fatal */
+ }
+ LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
+ flock(fd, LOCK_UN);
+ close(fd);
+ goto retry;
+ } else {
+ LOGV("DexOpt: good deps in cache file\n");
+ }
+ }
+
+ assert(fd >= 0);
+ return fd;
+
+close_fail:
+ flock(fd, LOCK_UN);
+ close(fd);
+ return -1;
+}
+
+/*
+ * Unlock the file descriptor.
+ *
+ * Returns "true" on success.
+ */
+bool dvmUnlockCachedDexFile(int fd)
+{
+ LOGVV("DexOpt: unlocking cache file fd=%d\n", fd);
+ return (flock(fd, LOCK_UN) == 0);
+}
+
+
+/*
+ * Given a descriptor for a file with DEX data in it, produce an
+ * optimized version.
+ *
+ * The file pointed to by "fd" is expected to be a locked shared resource
+ * (or private); we make no efforts to enforce multi-process correctness
+ * here.
+ *
+ * "fileName" is only used for debug output. "modWhen" and "crc" are stored
+ * in the dependency set.
+ *
+ * The "isBootstrap" flag determines how the optimizer and verifier handle
+ * package-scope access checks. When optimizing, we only load the bootstrap
+ * class DEX files and the target DEX, so the flag determines whether the
+ * target DEX classes are given a (synthetic) non-NULL classLoader pointer.
+ * This only really matters if the target DEX contains classes that claim to
+ * be in the same package as bootstrap classes.
+ *
+ * The optimizer will need to load every class in the target DEX file.
+ * This is generally undesirable, so we start a subprocess to do the
+ * work and wait for it to complete.
+ *
+ * Returns "true" on success. All data will have been written to "fd".
+ */
+bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
+ const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
+{
+ const char* lastPart = strrchr(fileName, '/');
+ if (lastPart != NULL)
+ lastPart++;
+ else
+ lastPart = fileName;
+
+ /*
+ * For basic optimizations (byte-swapping and structure aligning) we
+ * don't need to fork(). It looks like fork+exec is causing problems
+ * with gdb on our bewildered Linux distro, so in some situations we
+ * want to avoid this.
+ *
+ * For optimization and/or verification, we need to load all the classes.
+ *
+ * We don't check gDvm.generateRegisterMaps, since that is dependent
+ * upon the verifier state.
+ */
+ if (gDvm.classVerifyMode == VERIFY_MODE_NONE &&
+ (gDvm.dexOptMode == OPTIMIZE_MODE_NONE ||
+ gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED))
+ {
+ LOGD("DexOpt: --- BEGIN (quick) '%s' ---\n", lastPart);
+ return dvmContinueOptimization(fd, dexOffset, dexLength,
+ fileName, modWhen, crc, isBootstrap);
+ }
+
+
+ LOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---\n", lastPart, isBootstrap);
+
+ pid_t pid;
+
+ /*
+ * This could happen if something in our bootclasspath, which we thought
+ * was all optimized, got rejected.
+ */
+ if (gDvm.optimizing) {
+ LOGW("Rejecting recursive optimization attempt on '%s'\n", fileName);
+ return false;
+ }
+
+ pid = fork();
+ if (pid == 0) {
+ static const int kUseValgrind = 0;
+ static const char* kDexOptBin = "/bin/dexopt";
+ static const char* kValgrinder = "/usr/bin/valgrind";
+ static const int kFixedArgCount = 10;
+ static const int kValgrindArgCount = 5;
+ static const int kMaxIntLen = 12; // '-'+10dig+'\0' -OR- 0x+8dig
+ int bcpSize = dvmGetBootPathSize();
+ int argc = kFixedArgCount + bcpSize
+ + (kValgrindArgCount * kUseValgrind);
+ char* argv[argc+1]; // last entry is NULL
+ char values[argc][kMaxIntLen];
+ char* execFile;
+ char* androidRoot;
+ int flags;
+
+ /* change process groups, so we don't clash with ProcessManager */
+ setpgid(0, 0);
+
+ /* full path to optimizer */
+ androidRoot = getenv("ANDROID_ROOT");
+ if (androidRoot == NULL) {
+ LOGW("ANDROID_ROOT not set, defaulting to /system\n");
+ androidRoot = "/system";
+ }
+ execFile = malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
+ strcpy(execFile, androidRoot);
+ strcat(execFile, kDexOptBin);
+
+ /*
+ * Create arg vector.
+ */
+ int curArg = 0;
+
+ if (kUseValgrind) {
+ /* probably shouldn't ship the hard-coded path */
+ argv[curArg++] = (char*)kValgrinder;
+ argv[curArg++] = "--tool=memcheck";
+ argv[curArg++] = "--leak-check=yes"; // check for leaks too
+ argv[curArg++] = "--leak-resolution=med"; // increase from 2 to 4
+ argv[curArg++] = "--num-callers=16"; // default is 12
+ assert(curArg == kValgrindArgCount);
+ }
+ argv[curArg++] = execFile;
+
+ argv[curArg++] = "--dex";
+
+ sprintf(values[2], "%d", DALVIK_VM_BUILD);
+ argv[curArg++] = values[2];
+
+ sprintf(values[3], "%d", fd);
+ argv[curArg++] = values[3];
+
+ sprintf(values[4], "%d", (int) dexOffset);
+ argv[curArg++] = values[4];
+
+ sprintf(values[5], "%d", (int) dexLength);
+ argv[curArg++] = values[5];
+
+ argv[curArg++] = (char*)fileName;
+
+ sprintf(values[7], "%d", (int) modWhen);
+ argv[curArg++] = values[7];
+
+ sprintf(values[8], "%d", (int) crc);
+ argv[curArg++] = values[8];
+
+ flags = 0;
+ if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE) {
+ flags |= DEXOPT_OPT_ENABLED;
+ if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
+ flags |= DEXOPT_OPT_ALL;
+ }
+ if (gDvm.classVerifyMode != VERIFY_MODE_NONE) {
+ flags |= DEXOPT_VERIFY_ENABLED;
+ if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
+ flags |= DEXOPT_VERIFY_ALL;
+ }
+ if (isBootstrap)
+ flags |= DEXOPT_IS_BOOTSTRAP;
+ if (gDvm.generateRegisterMaps)
+ flags |= DEXOPT_GEN_REGISTER_MAP;
+ sprintf(values[9], "%d", flags);
+ argv[curArg++] = values[9];
+
+ assert(((!kUseValgrind && curArg == kFixedArgCount) ||
+ ((kUseValgrind && curArg == kFixedArgCount+kValgrindArgCount))));
+
+ ClassPathEntry* cpe;
+ for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+ argv[curArg++] = cpe->fileName;
+ }
+ assert(curArg == argc);
+
+ argv[curArg] = NULL;
+
+ if (kUseValgrind)
+ execv(kValgrinder, argv);
+ else
+ execv(execFile, argv);
+
+ LOGE("execv '%s'%s failed: %s\n", execFile,
+ kUseValgrind ? " [valgrind]" : "", strerror(errno));
+ exit(1);
+ } else {
+ LOGV("DexOpt: waiting for verify+opt, pid=%d\n", (int) pid);
+ int status;
+ pid_t gotPid;
+ int oldStatus;
+
+ /*
+ * Wait for the optimization process to finish. We go into VMWAIT
+ * mode here so GC suspension won't have to wait for us.
+ */
+ oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+ while (true) {
+ gotPid = waitpid(pid, &status, 0);
+ if (gotPid == -1 && errno == EINTR) {
+ LOGD("waitpid interrupted, retrying\n");
+ } else {
+ break;
+ }
+ }
+ dvmChangeStatus(NULL, oldStatus);
+ if (gotPid != pid) {
+ LOGE("waitpid failed: wanted %d, got %d: %s\n",
+ (int) pid, (int) gotPid, strerror(errno));
+ return false;
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ LOGD("DexOpt: --- END '%s' (success) ---\n", lastPart);
+ return true;
+ } else {
+ LOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed\n",
+ lastPart, status);
+ return false;
+ }
+ }
+}
+
+/*
+ * Do the actual optimization. This is called directly for "minimal"
+ * optimization, or from a newly-created process for "full" optimization.
+ *
+ * For best use of disk/memory, we want to extract once and perform
+ * optimizations in place. If the file has to expand or contract
+ * to match local structure padding/alignment expectations, we want
+ * to do the rewrite as part of the extract, rather than extracting
+ * into a temp file and slurping it back out. (The structure alignment
+ * is currently correct for all platforms, and this isn't expected to
+ * change, so we should be okay with having it already extracted.)
+ *
+ * Returns "true" on success.
+ */
+bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
+ const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
+{
+ DexClassLookup* pClassLookup = NULL;
+ IndexMapSet* pIndexMapSet = NULL;
+ RegisterMapBuilder* pRegMapBuilder = NULL;
+ bool doVerify, doOpt;
+ u4 headerFlags = 0;
+
+ if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
+ doVerify = false;
+ else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
+ doVerify = !isBootstrap;
+ else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
+ doVerify = true;
+
+ if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
+ doOpt = false;
+ else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
+ doOpt = doVerify;
+ else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
+ doOpt = true;
+
+ LOGV("Continuing optimization (%s, isb=%d, vfy=%d, opt=%d)\n",
+ fileName, isBootstrap, doVerify, doOpt);
+
+ assert(dexOffset >= 0);
+
+ /* quick test so we don't blow up on empty file */
+ if (dexLength < (int) sizeof(DexHeader)) {
+ LOGE("too small to be DEX\n");
+ return false;
+ }
+ if (dexOffset < (int) sizeof(DexOptHeader)) {
+ LOGE("not enough room for opt header\n");
+ return false;
+ }
+
+ bool result = false;
+
+ /*
+ * Drop this into a global so we don't have to pass it around. We could
+ * also add a field to DexFile, but since it only pertains to DEX
+ * creation that probably doesn't make sense.
+ */
+ gDvm.optimizingBootstrapClass = isBootstrap;
+
+ {
+ /*
+ * Map the entire file (so we don't have to worry about page
+ * alignment). The expectation is that the output file contains
+ * our DEX data plus room for a small header.
+ */
+ bool success;
+ void* mapAddr;
+ mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (mapAddr == MAP_FAILED) {
+ LOGE("unable to mmap DEX cache: %s\n", strerror(errno));
+ goto bail;
+ }
+
+ /*
+ * Rewrite the file. Byte reordering, structure realigning,
+ * class verification, and bytecode optimization are all performed
+ * here.
+ *
+ * In theory the file could change size and bits could shift around.
+ * In practice this would be annoying to deal with, so the file
+ * layout is designed so that it can always be rewritten in place.
+ *
+ * This sets "headerFlags" and creates the class lookup table as
+ * part of doing the processing.
+ */
+ success = dvmRewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
+ doVerify, doOpt, &headerFlags, &pClassLookup);
+
+ if (success) {
+ DvmDex* pDvmDex = NULL;
+ u1* dexAddr = ((u1*) mapAddr) + dexOffset;
+
+ if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
+ LOGE("Unable to create DexFile\n");
+ success = false;
+ } else {
+ /*
+ * If configured to do so, scan the instructions, looking
+ * for ways to reduce the size of the resolved-constant table.
+ * This is done post-optimization, across the instructions
+ * in all methods in all classes (even the ones that failed
+ * to load).
+ */
+ pIndexMapSet = dvmRewriteConstants(pDvmDex);
+
+ /*
+ * If configured to do so, generate a full set of register
+ * maps for all verified classes.
+ */
+ if (gDvm.generateRegisterMaps) {
+ pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);
+ if (pRegMapBuilder == NULL) {
+ LOGE("Failed generating register maps\n");
+ success = false;
+ }
+ }
+
+ DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;
+ updateChecksum(dexAddr, dexLength, pHeader);
+
+ dvmDexFileFree(pDvmDex);
+ }
+ }
+
+ /* unmap the read-write version, forcing writes to disk */
+ if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) {
+ LOGW("msync failed: %s\n", strerror(errno));
+ // weird, but keep going
+ }
+#if 1
+ /*
+ * This causes clean shutdown to fail, because we have loaded classes
+ * that point into it. For the optimizer this isn't a problem,
+ * because it's more efficient for the process to simply exit.
+ * Exclude this code when doing clean shutdown for valgrind.
+ */
+ if (munmap(mapAddr, dexOffset + dexLength) != 0) {
+ LOGE("munmap failed: %s\n", strerror(errno));
+ goto bail;
+ }
+#endif
+
+ if (!success)
+ goto bail;
+ }
+
+ /* get start offset, and adjust deps start for 64-bit alignment */
+ off_t depsOffset, auxOffset, endOffset, adjOffset;
+ int depsLength, auxLength;
+ u4 optChecksum;
+
+ depsOffset = lseek(fd, 0, SEEK_END);
+ if (depsOffset < 0) {
+ LOGE("lseek to EOF failed: %s\n", strerror(errno));
+ goto bail;
+ }
+ adjOffset = (depsOffset + 7) & ~(0x07);
+ if (adjOffset != depsOffset) {
+ LOGV("Adjusting deps start from %d to %d\n",
+ (int) depsOffset, (int) adjOffset);
+ depsOffset = adjOffset;
+ lseek(fd, depsOffset, SEEK_SET);
+ }
+
+ /*
+ * Append the dependency list.
+ */
+ if (writeDependencies(fd, modWhen, crc) != 0) {
+ LOGW("Failed writing dependencies\n");
+ goto bail;
+ }
+
+ /* compute deps length, then adjust aux start for 64-bit alignment */
+ auxOffset = lseek(fd, 0, SEEK_END);
+ depsLength = auxOffset - depsOffset;
+
+ adjOffset = (auxOffset + 7) & ~(0x07);
+ if (adjOffset != auxOffset) {
+ LOGV("Adjusting aux start from %d to %d\n",
+ (int) auxOffset, (int) adjOffset);
+ auxOffset = adjOffset;
+ lseek(fd, auxOffset, SEEK_SET);
+ }
+
+ /*
+ * Append any auxillary pre-computed data structures.
+ */
+ if (!writeAuxData(fd, pClassLookup, pIndexMapSet, pRegMapBuilder)) {
+ LOGW("Failed writing aux data\n");
+ goto bail;
+ }
+
+ endOffset = lseek(fd, 0, SEEK_END);
+ auxLength = endOffset - auxOffset;
+
+ /* compute checksum from start of deps to end of aux area */
+ if (!computeFileChecksum(fd, depsOffset,
+ (auxOffset+auxLength) - depsOffset, &optChecksum))
+ {
+ goto bail;
+ }
+
+ /*
+ * Output the "opt" header with all values filled in and a correct
+ * magic number.
+ */
+ DexOptHeader optHdr;
+ memset(&optHdr, 0xff, sizeof(optHdr));
+ memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);
+ memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);
+ optHdr.dexOffset = (u4) dexOffset;
+ optHdr.dexLength = (u4) dexLength;
+ optHdr.depsOffset = (u4) depsOffset;
+ optHdr.depsLength = (u4) depsLength;
+ optHdr.auxOffset = (u4) auxOffset;
+ optHdr.auxLength = (u4) auxLength;
+
+ optHdr.flags = headerFlags;
+ optHdr.checksum = optChecksum;
+
+ ssize_t actual;
+ lseek(fd, 0, SEEK_SET);
+ actual = write(fd, &optHdr, sizeof(optHdr));
+ if (actual != sizeof(optHdr)) {
+ logFailedWrite(sizeof(optHdr), actual, "opt header", errno);
+ goto bail;
+ }
+
+ LOGV("Successfully wrote DEX header\n");
+ result = true;
+
+ //dvmRegisterMapDumpStats();
+
+bail:
+ dvmFreeIndexMapSet(pIndexMapSet);
+ dvmFreeRegisterMapBuilder(pRegMapBuilder);
+ free(pClassLookup);
+ return result;
+}
+
+
+/*
+ * Get the cache file name from a ClassPathEntry.
+ */
+static const char* getCacheFileName(const ClassPathEntry* cpe)
+{
+ switch (cpe->kind) {
+ case kCpeJar:
+ return dvmGetJarFileCacheFileName((JarFile*) cpe->ptr);
+ case kCpeDex:
+ return dvmGetRawDexFileCacheFileName((RawDexFile*) cpe->ptr);
+ default:
+ LOGE("DexOpt: unexpected cpe kind %d\n", cpe->kind);
+ dvmAbort();
+ return NULL;
+ }
+}
+
+/*
+ * Get the SHA-1 signature.
+ */
+static const u1* getSignature(const ClassPathEntry* cpe)
+{
+ DvmDex* pDvmDex;
+
+ switch (cpe->kind) {
+ case kCpeJar:
+ pDvmDex = dvmGetJarFileDex((JarFile*) cpe->ptr);
+ break;
+ case kCpeDex:
+ pDvmDex = dvmGetRawDexFileDex((RawDexFile*) cpe->ptr);
+ break;
+ default:
+ LOGE("unexpected cpe kind %d\n", cpe->kind);
+ dvmAbort();
+ pDvmDex = NULL; // make gcc happy
+ }
+
+ assert(pDvmDex != NULL);
+ return pDvmDex->pDexFile->pHeader->signature;
+}
+
+
+/*
+ * Dependency layout:
+ * 4b Source file modification time, in seconds since 1970 UTC
+ * 4b CRC-32 from Zip entry, or Adler32 from source DEX header
+ * 4b Dalvik VM build number
+ * 4b Number of dependency entries that follow
+ * Dependency entries:
+ * 4b Name length (including terminating null)
+ * var Full path of cache entry (null terminated)
+ * 20b SHA-1 signature from source DEX file
+ *
+ * If this changes, update DEX_OPT_MAGIC_VERS.
+ */
+static const size_t kMinDepSize = 4 * 4;
+static const size_t kMaxDepSize = 4 * 4 + 1024; // sanity check
+
+/*
+ * Read the "opt" header, verify it, then read the dependencies section
+ * and verify that data as well.
+ *
+ * If "sourceAvail" is "true", this will verify that "modWhen" and "crc"
+ * match up with what is stored in the header. If they don't, we reject
+ * the file so that it can be recreated from the updated original. If
+ * "sourceAvail" isn't set, e.g. for a .odex file, we ignore these arguments.
+ *
+ * On successful return, the file will be seeked immediately past the
+ * "opt" header.
+ */
+bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
+ u4 crc, bool expectVerify, bool expectOpt)
+{
+ DexOptHeader optHdr;
+ u1* depData = NULL;
+ const u1* magic;
+ off_t posn;
+ int result = false;
+ ssize_t actual;
+
+ /*
+ * Start at the start. The "opt" header, when present, will always be
+ * the first thing in the file.
+ */
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ LOGE("DexOpt: failed to seek to start of file: %s\n", strerror(errno));
+ goto bail;
+ }
+
+ /*
+ * Read and do trivial verification on the opt header. The header is
+ * always in host byte order.
+ */
+ if (read(fd, &optHdr, sizeof(optHdr)) != sizeof(optHdr)) {
+ LOGE("DexOpt: failed reading opt header: %s\n", strerror(errno));
+ goto bail;
+ }
+
+ magic = optHdr.magic;
+ if (memcmp(magic, DEX_OPT_MAGIC, 4) != 0) {
+ /* not a DEX file, or previous attempt was interrupted */
+ LOGD("DexOpt: incorrect opt magic number (0x%02x %02x %02x %02x)\n",
+ magic[0], magic[1], magic[2], magic[3]);
+ goto bail;
+ }
+ if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
+ LOGW("DexOpt: stale opt version (0x%02x %02x %02x %02x)\n",
+ magic[4], magic[5], magic[6], magic[7]);
+ goto bail;
+ }
+ if (optHdr.depsLength < kMinDepSize || optHdr.depsLength > kMaxDepSize) {
+ LOGW("DexOpt: weird deps length %d, bailing\n", optHdr.depsLength);
+ goto bail;
+ }
+
+ /*
+ * Do the header flags match up with what we want?
+ *
+ * This is useful because it allows us to automatically regenerate
+ * a file when settings change (e.g. verification is now mandatory),
+ * but can cause difficulties if the bootstrap classes we depend upon
+ * were handled differently than the current options specify. We get
+ * upset because they're not verified or optimized, but we're not able
+ * to regenerate them because the installer won't let us.
+ *
+ * (This is also of limited value when !sourceAvail.)
+ *
+ * So, for now, we essentially ignore "expectVerify" and "expectOpt"
+ * by limiting the match mask.
+ *
+ * The only thing we really can't handle is incorrect byte-ordering.
+ */
+ const u4 matchMask = DEX_OPT_FLAG_BIG;
+ u4 expectedFlags = 0;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+ expectedFlags |= DEX_OPT_FLAG_BIG;
+#endif
+ if (expectVerify)
+ expectedFlags |= DEX_FLAG_VERIFIED;
+ if (expectOpt)
+ expectedFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
+ if ((expectedFlags & matchMask) != (optHdr.flags & matchMask)) {
+ LOGI("DexOpt: header flag mismatch (0x%02x vs 0x%02x, mask=0x%02x)\n",
+ expectedFlags, optHdr.flags, matchMask);
+ goto bail;
+ }
+
+ posn = lseek(fd, optHdr.depsOffset, SEEK_SET);
+ if (posn < 0) {
+ LOGW("DexOpt: seek to deps failed: %s\n", strerror(errno));
+ goto bail;
+ }
+
+ /*
+ * Read all of the dependency stuff into memory.
+ */
+ depData = (u1*) malloc(optHdr.depsLength);
+ if (depData == NULL) {
+ LOGW("DexOpt: unable to allocate %d bytes for deps\n",
+ optHdr.depsLength);
+ goto bail;
+ }
+ actual = read(fd, depData, optHdr.depsLength);
+ if (actual != (ssize_t) optHdr.depsLength) {
+ LOGW("DexOpt: failed reading deps: %d of %d (err=%s)\n",
+ (int) actual, optHdr.depsLength, strerror(errno));
+ goto bail;
+ }
+
+ /*
+ * Verify simple items.
+ */
+ const u1* ptr;
+ u4 val;
+
+ ptr = depData;
+ val = read4LE(&ptr);
+ if (sourceAvail && val != modWhen) {
+ LOGI("DexOpt: source file mod time mismatch (%08x vs %08x)\n",
+ val, modWhen);
+ goto bail;
+ }
+ val = read4LE(&ptr);
+ if (sourceAvail && val != crc) {
+ LOGI("DexOpt: source file CRC mismatch (%08x vs %08x)\n", val, crc);
+ goto bail;
+ }
+ val = read4LE(&ptr);
+ if (val != DALVIK_VM_BUILD) {
+ LOGD("DexOpt: VM build version mismatch (%d vs %d)\n",
+ val, DALVIK_VM_BUILD);
+ goto bail;
+ }
+
+ /*
+ * Verify dependencies on other cached DEX files. It must match
+ * exactly with what is currently defined in the bootclasspath.
+ */
+ ClassPathEntry* cpe;
+ u4 numDeps;
+
+ numDeps = read4LE(&ptr);
+ LOGV("+++ DexOpt: numDeps = %d\n", numDeps);
+ for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+ const char* cacheFileName = getCacheFileName(cpe);
+ const u1* signature = getSignature(cpe);
+ size_t len = strlen(cacheFileName) +1;
+ u4 storedStrLen;
+
+ if (numDeps == 0) {
+ /* more entries in bootclasspath than in deps list */
+ LOGI("DexOpt: not all deps represented\n");
+ goto bail;
+ }
+
+ storedStrLen = read4LE(&ptr);
+ if (len != storedStrLen ||
+ strcmp(cacheFileName, (const char*) ptr) != 0)
+ {
+ LOGI("DexOpt: mismatch dep name: '%s' vs. '%s'\n",
+ cacheFileName, ptr);
+ goto bail;
+ }
+
+ ptr += storedStrLen;
+
+ if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
+ LOGI("DexOpt: mismatch dep signature for '%s'\n", cacheFileName);
+ goto bail;
+ }
+ ptr += kSHA1DigestLen;
+
+ LOGV("DexOpt: dep match on '%s'\n", cacheFileName);
+
+ numDeps--;
+ }
+
+ if (numDeps != 0) {
+ /* more entries in deps list than in classpath */
+ LOGI("DexOpt: Some deps went away\n");
+ goto bail;
+ }
+
+ // consumed all data and no more?
+ if (ptr != depData + optHdr.depsLength) {
+ LOGW("DexOpt: Spurious dep data? %d vs %d\n",
+ (int) (ptr - depData), optHdr.depsLength);
+ assert(false);
+ }
+
+ result = true;
+
+bail:
+ free(depData);
+ return result;
+}
+
+/*
+ * Write the dependency info to "fd" at the current file position.
+ */
+static int writeDependencies(int fd, u4 modWhen, u4 crc)
+{
+ u1* buf = NULL;
+ ssize_t actual;
+ int result = -1;
+ ssize_t bufLen;
+ ClassPathEntry* cpe;
+ int i, numDeps;
+
+ /*
+ * Count up the number of completed entries in the bootclasspath.
+ */
+ numDeps = 0;
+ bufLen = 0;
+ for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+ const char* cacheFileName = getCacheFileName(cpe);
+ LOGV("+++ DexOpt: found dep '%s'\n", cacheFileName);
+
+ numDeps++;
+ bufLen += strlen(cacheFileName) +1;
+ }
+
+ bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
+
+ buf = malloc(bufLen);
+
+ set4LE(buf+0, modWhen);
+ set4LE(buf+4, crc);
+ set4LE(buf+8, DALVIK_VM_BUILD);
+ set4LE(buf+12, numDeps);
+
+ // TODO: do we want to add dvmGetInlineOpsTableLength() here? Won't
+ // help us if somebody replaces an existing entry, but it'd catch
+ // additions/removals.
+
+ u1* ptr = buf + 4*4;
+ for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+ const char* cacheFileName = getCacheFileName(cpe);
+ const u1* signature = getSignature(cpe);
+ int len = strlen(cacheFileName) +1;
+
+ if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
+ LOGE("DexOpt: overran buffer\n");
+ dvmAbort();
+ }
+
+ set4LE(ptr, len);
+ ptr += 4;
+ memcpy(ptr, cacheFileName, len);
+ ptr += len;
+ memcpy(ptr, signature, kSHA1DigestLen);
+ ptr += kSHA1DigestLen;
+ }
+
+ assert(ptr == buf + bufLen);
+
+ actual = write(fd, buf, bufLen);
+ if (actual != bufLen) {
+ result = (errno != 0) ? errno : -1;
+ logFailedWrite(bufLen, actual, "dep info", errno);
+ } else {
+ result = 0;
+ }
+
+ free(buf);
+ return result;
+}
+
+
+/*
+ * Write a block of data in "chunk" format.
+ *
+ * The chunk header fields are always in "native" byte order. If "size"
+ * is not a multiple of 8 bytes, the data area is padded out.
+ */
+static bool writeChunk(int fd, u4 type, const void* data, size_t size)
+{
+ ssize_t actual;
+ union { /* save a syscall by grouping these together */
+ char raw[8];
+ struct {
+ u4 type;
+ u4 size;
+ } ts;
+ } header;
+
+ assert(sizeof(header) == 8);
+
+ LOGV("Writing chunk, type=%.4s size=%d\n", (char*) &type, size);
+
+ header.ts.type = type;
+ header.ts.size = (u4) size;
+ actual = write(fd, &header, sizeof(header));
+ if (actual != sizeof(header)) {
+ logFailedWrite(size, actual, "aux chunk header write", errno);
+ return false;
+ }
+
+ if (size > 0) {
+ actual = write(fd, data, size);
+ if (actual != (ssize_t) size) {
+ logFailedWrite(size, actual, "aux chunk write", errno);
+ return false;
+ }
+ }
+
+ /* if necessary, pad to 64-bit alignment */
+ if ((size & 7) != 0) {
+ int padSize = 8 - (size & 7);
+ LOGV("size was %d, inserting %d pad bytes\n", size, padSize);
+ lseek(fd, padSize, SEEK_CUR);
+ }
+
+ assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
+
+ return true;
+}
+
+/*
+ * Write aux data.
+ *
+ * We have different pieces, some of which may be optional. To make the
+ * most effective use of space, we use a "chunk" format, with a 4-byte
+ * type and a 4-byte length. We guarantee 64-bit alignment for the data,
+ * so it can be used directly when the file is mapped for reading.
+ */
+static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,
+ const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder)
+{
+ /* pre-computed class lookup hash table */
+ if (!writeChunk(fd, (u4) kDexChunkClassLookup,
+ pClassLookup, pClassLookup->size))
+ {
+ return false;
+ }
+
+ /* remapped constants (optional) */
+ if (pIndexMapSet != NULL) {
+ if (!writeChunk(fd, pIndexMapSet->chunkType,
+ pIndexMapSet->chunkData, pIndexMapSet->chunkDataLen))
+ {
+ return false;
+ }
+ }
+
+ /* register maps (optional) */
+ if (pRegMapBuilder != NULL) {
+ if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
+ pRegMapBuilder->data, pRegMapBuilder->size))
+ {
+ return false;
+ }
+ }
+
+ /* write the end marker */
+ if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Log a failed write.
+ */
+static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
+ int err)
+{
+ LOGE("Write failed: %s (%d of %d): %s\n",
+ msg, (int)actual, (int)expected, strerror(err));
+}
+
+/*
+ * Compute a checksum on a piece of an open file.
+ *
+ * File will be positioned at end of checksummed area.
+ *
+ * Returns "true" on success.
+ */
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum)
+{
+ unsigned char readBuf[8192];
+ ssize_t actual;
+ uLong adler;
+
+ if (lseek(fd, start, SEEK_SET) != start) {
+ LOGE("Unable to seek to start of checksum area (%ld): %s\n",
+ (long) start, strerror(errno));
+ return false;
+ }
+
+ adler = adler32(0L, Z_NULL, 0);
+
+ while (length != 0) {
+ size_t wanted = (length < sizeof(readBuf)) ? length : sizeof(readBuf);
+ actual = read(fd, readBuf, wanted);
+ if (actual <= 0) {
+ LOGE("Read failed (%d) while computing checksum (len=%zu): %s\n",
+ (int) actual, length, strerror(errno));
+ return false;
+ }
+
+ adler = adler32(adler, readBuf, actual);
+
+ length -= actual;
+ }
+
+ *pSum = adler;
+ return true;
+}
+
+/*
+ * Update the Adler-32 checksum stored in the DEX file. This covers the
+ * swapped and optimized DEX data, but does not include the opt header
+ * or auxillary data.
+ */
+static void updateChecksum(u1* addr, int len, DexHeader* pHeader)
+{
+ /*
+ * Rewrite the checksum. We leave the SHA-1 signature alone.
+ */
+ uLong adler = adler32(0L, Z_NULL, 0);
+ const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+
+ adler = adler32(adler, addr + nonSum, len - nonSum);
+ pHeader->checksum = adler;
+}
+
diff --git a/vm/analysis/DexOptimize.h b/vm/analysis/DexPrepare.h
similarity index 84%
rename from vm/analysis/DexOptimize.h
rename to vm/analysis/DexPrepare.h
index afcb3cb..ae94979 100644
--- a/vm/analysis/DexOptimize.h
+++ b/vm/analysis/DexPrepare.h
@@ -15,10 +15,10 @@
*/
/*
- * DEX optimization declarations.
+ * DEX preparation declarations.
*/
-#ifndef _DALVIK_DEXOPTIMIZE
-#define _DALVIK_DEXOPTIMIZE
+#ifndef _DALVIK_DEXPREPARE
+#define _DALVIK_DEXPREPARE
/*
* Global DEX optimizer control. Determines the circumstances in which we
@@ -112,18 +112,4 @@
bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
const char* fileName, u4 modWhen, u4 crc, bool isBootstrap);
-/*
- * Abbreviated resolution functions, for use by optimization and verification
- * code.
- */
-ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
- VerifyError* pFailure);
-Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
- MethodType methodType, VerifyError* pFailure);
-Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx);
-InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
- VerifyError* pFailure);
-StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
- VerifyError* pFailure);
-
-#endif /*_DALVIK_DEXOPTIMIZE*/
+#endif /*_DALVIK_DEXPREPARE*/
diff --git a/vm/analysis/Optimize.c b/vm/analysis/Optimize.c
new file mode 100644
index 0000000..7121229
--- /dev/null
+++ b/vm/analysis/Optimize.c
@@ -0,0 +1,1199 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Perform some simple bytecode optimizations, chiefly "quickening" of
+ * opcodes.
+ */
+#include "Dalvik.h"
+#include "libdex/InstrUtils.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+
+/*
+ * Virtual/direct calls to "method" are replaced with an execute-inline
+ * instruction with index "idx".
+ */
+typedef struct InlineSub {
+ Method* method;
+ int inlineIdx;
+} InlineSub;
+
+
+/* fwd */
+static bool loadAllClasses(DvmDex* pDvmDex);
+static void optimizeLoadedClasses(DexFile* pDexFile);
+static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs);
+static bool optimizeMethod(Method* method, const InlineSub* inlineSubs);
+static void rewriteInstField(Method* method, u2* insns, OpCode newOpc);
+static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc);
+static bool rewriteEmptyDirectInvoke(Method* method, u2* insns);
+static bool rewriteExecuteInline(Method* method, u2* insns,
+ MethodType methodType, const InlineSub* inlineSubs);
+static bool rewriteExecuteInlineRange(Method* method, u2* insns,
+ MethodType methodType, const InlineSub* inlineSubs);
+
+
+/*
+ * Perform in-place rewrites on a memory-mapped DEX file.
+ *
+ * This happens in a short-lived child process, so we can go nutty with
+ * loading classes and allocating memory.
+ */
+bool dvmRewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
+ u4* pHeaderFlags, DexClassLookup** ppClassLookup)
+{
+ u8 prepWhen, loadWhen, verifyWhen, optWhen;
+ DvmDex* pDvmDex = NULL;
+ bool result = false;
+
+ *pHeaderFlags = 0;
+
+ LOGV("+++ swapping bytes\n");
+ if (dexFixByteOrdering(addr, len) != 0)
+ goto bail;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+ *pHeaderFlags |= DEX_OPT_FLAG_BIG;
+#endif
+
+ /*
+ * Now that the DEX file can be read directly, create a DexFile for it.
+ */
+ if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
+ LOGE("Unable to create DexFile\n");
+ goto bail;
+ }
+
+ /*
+ * Create the class lookup table.
+ */
+ //startWhen = dvmGetRelativeTimeUsec();
+ *ppClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
+ if (*ppClassLookup == NULL)
+ goto bail;
+
+ /*
+ * Bail out early if they don't want The Works. The current implementation
+ * doesn't fork a new process if this flag isn't set, so we really don't
+ * want to continue on with the crazy class loading.
+ */
+ if (!doVerify && !doOpt) {
+ result = true;
+ goto bail;
+ }
+
+ /* this is needed for the next part */
+ pDvmDex->pDexFile->pClassLookup = *ppClassLookup;
+
+ prepWhen = dvmGetRelativeTimeUsec();
+
+ /*
+ * Load all classes found in this DEX file. If they fail to load for
+ * some reason, they won't get verified (which is as it should be).
+ */
+ if (!loadAllClasses(pDvmDex))
+ goto bail;
+ loadWhen = dvmGetRelativeTimeUsec();
+
+ /*
+ * Verify all classes in the DEX file. Export the "is verified" flag
+ * to the DEX file we're creating.
+ */
+ if (doVerify) {
+ dvmVerifyAllClasses(pDvmDex->pDexFile);
+ *pHeaderFlags |= DEX_FLAG_VERIFIED;
+ }
+ verifyWhen = dvmGetRelativeTimeUsec();
+
+ /*
+ * Optimize the classes we successfully loaded. If the opt mode is
+ * OPTIMIZE_MODE_VERIFIED, each class must have been successfully
+ * verified or we'll skip it.
+ */
+#ifndef PROFILE_FIELD_ACCESS
+ if (doOpt) {
+ optimizeLoadedClasses(pDvmDex->pDexFile);
+ *pHeaderFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
+ }
+#endif
+ optWhen = dvmGetRelativeTimeUsec();
+
+ LOGD("DexOpt: load %dms, verify %dms, opt %dms\n",
+ (int) (loadWhen - prepWhen) / 1000,
+ (int) (verifyWhen - loadWhen) / 1000,
+ (int) (optWhen - verifyWhen) / 1000);
+
+ result = true;
+
+bail:
+ /* free up storage */
+ dvmDexFileFree(pDvmDex);
+
+ return result;
+}
+
+/*
+ * Try to load all classes in the specified DEX. If they have some sort
+ * of broken dependency, e.g. their superclass lives in a different DEX
+ * that wasn't previously loaded into the bootstrap class path, loading
+ * will fail. This is the desired behavior.
+ *
+ * We have no notion of class loader at this point, so we load all of
+ * the classes with the bootstrap class loader. It turns out this has
+ * exactly the behavior we want, and has no ill side effects because we're
+ * running in a separate process and anything we load here will be forgotten.
+ *
+ * We set the CLASS_MULTIPLE_DEFS flag here if we see multiple definitions.
+ * This works because we only call here as part of optimization / pre-verify,
+ * not during verification as part of loading a class into a running VM.
+ *
+ * This returns "false" if the world is too screwed up to do anything
+ * useful at all.
+ */
+static bool loadAllClasses(DvmDex* pDvmDex)
+{
+ u4 count = pDvmDex->pDexFile->pHeader->classDefsSize;
+ u4 idx;
+ int loaded = 0;
+
+ LOGV("DexOpt: +++ trying to load %d classes\n", count);
+
+ dvmSetBootPathExtraDex(pDvmDex);
+
+ /*
+ * We have some circularity issues with Class and Object that are most
+ * easily avoided by ensuring that Object is never the first thing we
+ * try to find. Take care of that here. (We only need to do this when
+ * loading classes from the DEX file that contains Object, and only
+ * when Object comes first in the list, but it costs very little to
+ * do it in all cases.)
+ */
+ if (dvmFindSystemClass("Ljava/lang/Class;") == NULL) {
+ LOGE("ERROR: java.lang.Class does not exist!\n");
+ return false;
+ }
+
+ for (idx = 0; idx < count; idx++) {
+ const DexClassDef* pClassDef;
+ const char* classDescriptor;
+ ClassObject* newClass;
+
+ pClassDef = dexGetClassDef(pDvmDex->pDexFile, idx);
+ classDescriptor =
+ dexStringByTypeIdx(pDvmDex->pDexFile, pClassDef->classIdx);
+
+ LOGV("+++ loading '%s'", classDescriptor);
+ //newClass = dvmDefineClass(pDexFile, classDescriptor,
+ // NULL);
+ newClass = dvmFindSystemClassNoInit(classDescriptor);
+ if (newClass == NULL) {
+ LOGV("DexOpt: failed loading '%s'\n", classDescriptor);
+ dvmClearOptException(dvmThreadSelf());
+ } else if (newClass->pDvmDex != pDvmDex) {
+ /*
+ * We don't load the new one, and we tag the first one found
+ * with the "multiple def" flag so the resolver doesn't try
+ * to make it available.
+ */
+ LOGD("DexOpt: '%s' has an earlier definition; blocking out\n",
+ classDescriptor);
+ SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
+ } else {
+ loaded++;
+ }
+ }
+ LOGV("DexOpt: +++ successfully loaded %d classes\n", loaded);
+
+ dvmSetBootPathExtraDex(NULL);
+ return true;
+}
+
+
+/*
+ * Create a table of inline substitutions.
+ *
+ * TODO: this is currently just a linear array. We will want to put this
+ * into a hash table as the list size increases.
+ */
+static InlineSub* createInlineSubsTable(void)
+{
+ const InlineOperation* ops = dvmGetInlineOpsTable();
+ const int count = dvmGetInlineOpsTableLength();
+ InlineSub* table;
+ Method* method;
+ ClassObject* clazz;
+ int i, tableIndex;
+
+ /*
+ * Allocate for optimism: one slot per entry, plus an end-of-list marker.
+ */
+ table = malloc(sizeof(InlineSub) * (count+1));
+
+ tableIndex = 0;
+ for (i = 0; i < count; i++) {
+ clazz = dvmFindClassNoInit(ops[i].classDescriptor, NULL);
+ if (clazz == NULL) {
+ LOGV("DexOpt: can't inline for class '%s': not found\n",
+ ops[i].classDescriptor);
+ dvmClearOptException(dvmThreadSelf());
+ } else {
+ /*
+ * Method could be virtual or direct. Try both. Don't use
+ * the "hier" versions.
+ */
+ method = dvmFindDirectMethodByDescriptor(clazz, ops[i].methodName,
+ ops[i].methodSignature);
+ if (method == NULL)
+ method = dvmFindVirtualMethodByDescriptor(clazz, ops[i].methodName,
+ ops[i].methodSignature);
+ if (method == NULL) {
+ LOGW("DexOpt: can't inline %s.%s %s: method not found\n",
+ ops[i].classDescriptor, ops[i].methodName,
+ ops[i].methodSignature);
+ } else {
+ if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
+ LOGW("DexOpt: WARNING: inline op on non-final class/method "
+ "%s.%s\n",
+ clazz->descriptor, method->name);
+ /* fail? */
+ }
+ if (dvmIsSynchronizedMethod(method) ||
+ dvmIsDeclaredSynchronizedMethod(method))
+ {
+ LOGW("DexOpt: WARNING: inline op on synchronized method "
+ "%s.%s\n",
+ clazz->descriptor, method->name);
+ /* fail? */
+ }
+
+ table[tableIndex].method = method;
+ table[tableIndex].inlineIdx = i;
+ tableIndex++;
+
+ LOGV("DexOpt: will inline %d: %s.%s %s\n", i,
+ ops[i].classDescriptor, ops[i].methodName,
+ ops[i].methodSignature);
+ }
+ }
+ }
+
+ /* mark end of table */
+ table[tableIndex].method = NULL;
+ LOGV("DexOpt: inline table has %d entries\n", tableIndex);
+
+ return table;
+}
+
+/*
+ * Run through all classes that were successfully loaded from this DEX
+ * file and optimize their code sections.
+ */
+static void optimizeLoadedClasses(DexFile* pDexFile)
+{
+ u4 count = pDexFile->pHeader->classDefsSize;
+ u4 idx;
+ InlineSub* inlineSubs = NULL;
+
+ LOGV("DexOpt: +++ optimizing up to %d classes\n", count);
+ assert(gDvm.dexOptMode != OPTIMIZE_MODE_NONE);
+
+ inlineSubs = createInlineSubsTable();
+
+ for (idx = 0; idx < count; idx++) {
+ const DexClassDef* pClassDef;
+ const char* classDescriptor;
+ ClassObject* clazz;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+ classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ /* all classes are loaded into the bootstrap class loader */
+ clazz = dvmLookupClass(classDescriptor, NULL, false);
+ if (clazz != NULL) {
+ if ((pClassDef->accessFlags & CLASS_ISPREVERIFIED) == 0 &&
+ gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
+ {
+ LOGV("DexOpt: not optimizing '%s': not verified\n",
+ classDescriptor);
+ } else if (clazz->pDvmDex->pDexFile != pDexFile) {
+ /* shouldn't be here -- verifier should have caught */
+ LOGD("DexOpt: not optimizing '%s': multiple definitions\n",
+ classDescriptor);
+ } else {
+ optimizeClass(clazz, inlineSubs);
+
+ /* set the flag whether or not we actually did anything */
+ ((DexClassDef*)pClassDef)->accessFlags |=
+ CLASS_ISOPTIMIZED;
+ }
+ } else {
+ LOGV("DexOpt: not optimizing unavailable class '%s'\n",
+ classDescriptor);
+ }
+ }
+
+ free(inlineSubs);
+}
+
+/*
+ * Optimize the specified class.
+ */
+static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs)
+{
+ int i;
+
+ for (i = 0; i < clazz->directMethodCount; i++) {
+ if (!optimizeMethod(&clazz->directMethods[i], inlineSubs))
+ goto fail;
+ }
+ for (i = 0; i < clazz->virtualMethodCount; i++) {
+ if (!optimizeMethod(&clazz->virtualMethods[i], inlineSubs))
+ goto fail;
+ }
+
+ return;
+
+fail:
+ LOGV("DexOpt: ceasing optimization attempts on %s\n", clazz->descriptor);
+}
+
+/*
+ * Optimize instructions in a method.
+ *
+ * Returns "true" if all went well, "false" if we bailed out early when
+ * something failed.
+ */
+static bool optimizeMethod(Method* method, const InlineSub* inlineSubs)
+{
+ u4 insnsSize;
+ u2* insns;
+ u2 inst;
+
+ if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
+ return true;
+
+ insns = (u2*) method->insns;
+ assert(insns != NULL);
+ insnsSize = dvmGetMethodInsnsSize(method);
+
+ while (insnsSize > 0) {
+ int width;
+
+ inst = *insns & 0xff;
+
+ switch (inst) {
+ case OP_IGET:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ rewriteInstField(method, insns, OP_IGET_QUICK);
+ break;
+ case OP_IGET_WIDE:
+ rewriteInstField(method, insns, OP_IGET_WIDE_QUICK);
+ break;
+ case OP_IGET_OBJECT:
+ rewriteInstField(method, insns, OP_IGET_OBJECT_QUICK);
+ break;
+ case OP_IPUT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ rewriteInstField(method, insns, OP_IPUT_QUICK);
+ break;
+ case OP_IPUT_WIDE:
+ rewriteInstField(method, insns, OP_IPUT_WIDE_QUICK);
+ break;
+ case OP_IPUT_OBJECT:
+ rewriteInstField(method, insns, OP_IPUT_OBJECT_QUICK);
+ break;
+
+ case OP_INVOKE_VIRTUAL:
+ if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL,inlineSubs))
+ {
+ if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_VIRTUAL_QUICK))
+ return false;
+ }
+ break;
+ case OP_INVOKE_VIRTUAL_RANGE:
+ if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL,
+ inlineSubs))
+ {
+ if (!rewriteVirtualInvoke(method, insns,
+ OP_INVOKE_VIRTUAL_QUICK_RANGE))
+ {
+ return false;
+ }
+ }
+ break;
+ case OP_INVOKE_SUPER:
+ if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK))
+ return false;
+ break;
+ case OP_INVOKE_SUPER_RANGE:
+ if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE))
+ return false;
+ break;
+
+ case OP_INVOKE_DIRECT:
+ if (!rewriteExecuteInline(method, insns, METHOD_DIRECT, inlineSubs))
+ {
+ if (!rewriteEmptyDirectInvoke(method, insns))
+ return false;
+ }
+ break;
+ case OP_INVOKE_DIRECT_RANGE:
+ rewriteExecuteInlineRange(method, insns, METHOD_DIRECT, inlineSubs);
+ break;
+
+ case OP_INVOKE_STATIC:
+ rewriteExecuteInline(method, insns, METHOD_STATIC, inlineSubs);
+ break;
+ case OP_INVOKE_STATIC_RANGE:
+ rewriteExecuteInlineRange(method, insns, METHOD_STATIC, inlineSubs);
+ break;
+
+ default:
+ // ignore this instruction
+ ;
+ }
+
+ if (*insns == kPackedSwitchSignature) {
+ width = 4 + insns[1] * 2;
+ } else if (*insns == kSparseSwitchSignature) {
+ width = 2 + insns[1] * 4;
+ } else if (*insns == kArrayDataSignature) {
+ u2 elemWidth = insns[1];
+ u4 len = insns[2] | (((u4)insns[3]) << 16);
+ width = 4 + (elemWidth * len + 1) / 2;
+ } else {
+ width = dexGetInstrWidthAbs(gDvm.instrWidth, inst);
+ }
+ assert(width > 0);
+
+ insns += width;
+ insnsSize -= width;
+ }
+
+ assert(insnsSize == 0);
+ return true;
+}
+
+
+/*
+ * If "referrer" and "resClass" don't come from the same DEX file, and
+ * the DEX we're working on is not destined for the bootstrap class path,
+ * tweak the class loader so package-access checks work correctly.
+ *
+ * Only do this if we're doing pre-verification or optimization.
+ */
+static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
+{
+ if (!gDvm.optimizing)
+ return;
+ assert(referrer->classLoader == NULL);
+ assert(resClass->classLoader == NULL);
+
+ if (!gDvm.optimizingBootstrapClass) {
+ /* class loader for an array class comes from element type */
+ if (dvmIsArrayClass(resClass))
+ resClass = resClass->elementClass;
+ if (referrer->pDvmDex != resClass->pDvmDex)
+ resClass->classLoader = (Object*) 0xdead3333;
+ }
+}
+
+/*
+ * Undo the effects of tweakLoader.
+ */
+static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
+{
+ if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
+ return;
+
+ if (dvmIsArrayClass(resClass))
+ resClass = resClass->elementClass;
+ resClass->classLoader = NULL;
+}
+
+
+/*
+ * Alternate version of dvmResolveClass for use with verification and
+ * optimization. Performs access checks on every resolve, and refuses
+ * to acknowledge the existence of classes defined in more than one DEX
+ * file.
+ *
+ * Exceptions caused by failures are cleared before returning.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
+ VerifyError* pFailure)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ ClassObject* resClass;
+
+ /*
+ * Check the table first. If not there, do the lookup by name.
+ */
+ resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
+ if (resClass == NULL) {
+ const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
+ if (className[0] != '\0' && className[1] == '\0') {
+ /* primitive type */
+ resClass = dvmFindPrimitiveClass(className[0]);
+ } else {
+ resClass = dvmFindClassNoInit(className, referrer->classLoader);
+ }
+ if (resClass == NULL) {
+ /* not found, exception should be raised */
+ LOGV("DexOpt: class %d (%s) not found\n",
+ classIdx,
+ dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
+ if (pFailure != NULL) {
+ /* dig through the wrappers to find the original failure */
+ Object* excep = dvmGetException(dvmThreadSelf());
+ while (true) {
+ Object* cause = dvmGetExceptionCause(excep);
+ if (cause == NULL)
+ break;
+ excep = cause;
+ }
+ if (strcmp(excep->clazz->descriptor,
+ "Ljava/lang/IncompatibleClassChangeError;") == 0)
+ {
+ *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+ } else {
+ *pFailure = VERIFY_ERROR_NO_CLASS;
+ }
+ }
+ dvmClearOptException(dvmThreadSelf());
+ return NULL;
+ }
+
+ /*
+ * Add it to the resolved table so we're faster on the next lookup.
+ */
+ dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
+ }
+
+ /* multiple definitions? */
+ if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
+ LOGI("DexOpt: not resolving ambiguous class '%s'\n",
+ resClass->descriptor);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_NO_CLASS;
+ return NULL;
+ }
+
+ /* access allowed? */
+ tweakLoader(referrer, resClass);
+ bool allowed = dvmCheckClassAccess(referrer, resClass);
+ untweakLoader(referrer, resClass);
+ if (!allowed) {
+ LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
+ referrer->descriptor, resClass->descriptor);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_ACCESS_CLASS;
+ return NULL;
+ }
+
+ return resClass;
+}
+
+/*
+ * Alternate version of dvmResolveInstField().
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
+ VerifyError* pFailure)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ InstField* resField;
+
+ resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
+ if (resField == NULL) {
+ const DexFieldId* pFieldId;
+ ClassObject* resClass;
+
+ pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
+
+ /*
+ * Find the field's class.
+ */
+ resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
+ if (resClass == NULL) {
+ //dvmClearOptException(dvmThreadSelf());
+ assert(!dvmCheckException(dvmThreadSelf()));
+ if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+ return NULL;
+ }
+
+ resField = (InstField*)dvmFindFieldHier(resClass,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+ dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+ if (resField == NULL) {
+ LOGD("DexOpt: couldn't find field %s.%s\n",
+ resClass->descriptor,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_NO_FIELD;
+ return NULL;
+ }
+ if (dvmIsStaticField(&resField->field)) {
+ LOGD("DexOpt: wanted instance, got static for field %s.%s\n",
+ resClass->descriptor,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+ return NULL;
+ }
+
+ /*
+ * Add it to the resolved table so we're faster on the next lookup.
+ */
+ dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
+ }
+
+ /* access allowed? */
+ tweakLoader(referrer, resField->field.clazz);
+ bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+ untweakLoader(referrer, resField->field.clazz);
+ if (!allowed) {
+ LOGI("DexOpt: access denied from %s to field %s.%s\n",
+ referrer->descriptor, resField->field.clazz->descriptor,
+ resField->field.name);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+ return NULL;
+ }
+
+ return resField;
+}
+
+/*
+ * Alternate version of dvmResolveStaticField().
+ *
+ * Does not force initialization of the resolved field's class.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
+ VerifyError* pFailure)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ StaticField* resField;
+
+ resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
+ if (resField == NULL) {
+ const DexFieldId* pFieldId;
+ ClassObject* resClass;
+
+ pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
+
+ /*
+ * Find the field's class.
+ */
+ resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
+ if (resClass == NULL) {
+ //dvmClearOptException(dvmThreadSelf());
+ assert(!dvmCheckException(dvmThreadSelf()));
+ if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+ return NULL;
+ }
+
+ resField = (StaticField*)dvmFindFieldHier(resClass,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+ dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+ if (resField == NULL) {
+ LOGD("DexOpt: couldn't find static field\n");
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_NO_FIELD;
+ return NULL;
+ }
+ if (!dvmIsStaticField(&resField->field)) {
+ LOGD("DexOpt: wanted static, got instance for field %s.%s\n",
+ resClass->descriptor,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+ return NULL;
+ }
+
+ /*
+ * Add it to the resolved table so we're faster on the next lookup.
+ *
+ * We can only do this if we're in "dexopt", because the presence
+ * of a valid value in the resolution table implies that the class
+ * containing the static field has been initialized.
+ */
+ if (gDvm.optimizing)
+ dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
+ }
+
+ /* access allowed? */
+ tweakLoader(referrer, resField->field.clazz);
+ bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+ untweakLoader(referrer, resField->field.clazz);
+ if (!allowed) {
+ LOGI("DexOpt: access denied from %s to field %s.%s\n",
+ referrer->descriptor, resField->field.clazz->descriptor,
+ resField->field.name);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+ return NULL;
+ }
+
+ return resField;
+}
+
+
+/*
+ * Rewrite an iget/iput instruction. These all have the form:
+ * op vA, vB, field@CCCC
+ *
+ * Where vA holds the value, vB holds the object reference, and CCCC is
+ * the field reference constant pool offset. We want to replace CCCC
+ * with the byte offset from the start of the object.
+ *
+ * "clazz" is the referring class. We need this because we verify
+ * access rights here.
+ */
+static void rewriteInstField(Method* method, u2* insns, OpCode newOpc)
+{
+ ClassObject* clazz = method->clazz;
+ u2 fieldIdx = insns[1];
+ InstField* field;
+ int byteOffset;
+
+ field = dvmOptResolveInstField(clazz, fieldIdx, NULL);
+ if (field == NULL) {
+ LOGI("DexOpt: unable to optimize field ref 0x%04x at 0x%02x in %s.%s\n",
+ fieldIdx, (int) (insns - method->insns), clazz->descriptor,
+ method->name);
+ return;
+ }
+
+ if (field->byteOffset >= 65536) {
+ LOGI("DexOpt: field offset exceeds 64K (%d)\n", field->byteOffset);
+ return;
+ }
+
+ insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
+ insns[1] = (u2) field->byteOffset;
+ LOGVV("DexOpt: rewrote access to %s.%s --> %d\n",
+ field->field.clazz->descriptor, field->field.name,
+ field->byteOffset);
+}
+
+/*
+ * Alternate version of dvmResolveMethod().
+ *
+ * Doesn't throw exceptions, and checks access on every lookup.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
+ MethodType methodType, VerifyError* pFailure)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ Method* resMethod;
+
+ assert(methodType == METHOD_DIRECT ||
+ methodType == METHOD_VIRTUAL ||
+ methodType == METHOD_STATIC);
+
+ LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx,
+ referrer->descriptor);
+
+ resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+ if (resMethod == NULL) {
+ const DexMethodId* pMethodId;
+ ClassObject* resClass;
+
+ pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+ resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
+ if (resClass == NULL) {
+ /*
+ * Can't find the class that the method is a part of, or don't
+ * have permission to access the class.
+ */
+ LOGV("DexOpt: can't find called method's class (?.%s)\n",
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
+ if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+ return NULL;
+ }
+ if (dvmIsInterfaceClass(resClass)) {
+ /* method is part of an interface; this is wrong method for that */
+ LOGW("DexOpt: method is in an interface\n");
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+
+ /*
+ * We need to chase up the class hierarchy to find methods defined
+ * in super-classes. (We only want to check the current class
+ * if we're looking for a constructor.)
+ */
+ DexProto proto;
+ dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+ if (methodType == METHOD_DIRECT) {
+ resMethod = dvmFindDirectMethod(resClass,
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
+ } else {
+ /* METHOD_STATIC or METHOD_VIRTUAL */
+ resMethod = dvmFindMethodHier(resClass,
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
+ }
+
+ if (resMethod == NULL) {
+ LOGV("DexOpt: couldn't find method '%s'\n",
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_NO_METHOD;
+ return NULL;
+ }
+ if (methodType == METHOD_STATIC) {
+ if (!dvmIsStaticMethod(resMethod)) {
+ LOGD("DexOpt: wanted static, got instance for method %s.%s\n",
+ resClass->descriptor, resMethod->name);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+ return NULL;
+ }
+ } else if (methodType == METHOD_VIRTUAL) {
+ if (dvmIsStaticMethod(resMethod)) {
+ LOGD("DexOpt: wanted instance, got static for method %s.%s\n",
+ resClass->descriptor, resMethod->name);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+ return NULL;
+ }
+ }
+
+ /* see if this is a pure-abstract method */
+ if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
+ LOGW("DexOpt: pure-abstract method '%s' in %s\n",
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
+ resClass->descriptor);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+
+ /*
+ * Add it to the resolved table so we're faster on the next lookup.
+ *
+ * We can only do this for static methods if we're not in "dexopt",
+ * because the presence of a valid value in the resolution table
+ * implies that the class containing the static field has been
+ * initialized.
+ */
+ if (methodType != METHOD_STATIC || gDvm.optimizing)
+ dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+ }
+
+ LOGVV("--- found method %d (%s.%s)\n",
+ methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+ /* access allowed? */
+ tweakLoader(referrer, resMethod->clazz);
+ bool allowed = dvmCheckMethodAccess(referrer, resMethod);
+ untweakLoader(referrer, resMethod->clazz);
+ if (!allowed) {
+ IF_LOGI() {
+ char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+ LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
+ resMethod->clazz->descriptor, resMethod->name, desc,
+ referrer->descriptor);
+ free(desc);
+ }
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_ACCESS_METHOD;
+ return NULL;
+ }
+
+ return resMethod;
+}
+
+/*
+ * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
+ * invoke-super/range. These all have the form:
+ * op vAA, meth@BBBB, reg stuff @CCCC
+ *
+ * We want to replace the method constant pool index BBBB with the
+ * vtable index.
+ */
+static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc)
+{
+ ClassObject* clazz = method->clazz;
+ Method* baseMethod;
+ u2 methodIdx = insns[1];
+
+ baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
+ if (baseMethod == NULL) {
+ LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n",
+ methodIdx,
+ (int) (insns - method->insns), clazz->descriptor,
+ method->name);
+ return false;
+ }
+
+ assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
+ (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
+ (insns[0] & 0xff) == OP_INVOKE_SUPER ||
+ (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
+
+ /*
+ * Note: Method->methodIndex is a u2 and is range checked during the
+ * initial load.
+ */
+ insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
+ insns[1] = baseMethod->methodIndex;
+
+ //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
+ // method->clazz->descriptor, method->name,
+ // baseMethod->clazz->descriptor, baseMethod->name);
+
+ return true;
+}
+
+/*
+ * Rewrite invoke-direct, which has the form:
+ * op vAA, meth@BBBB, reg stuff @CCCC
+ *
+ * There isn't a lot we can do to make this faster, but in some situations
+ * we can make it go away entirely.
+ *
+ * This must only be used when the invoked method does nothing and has
+ * no return value (the latter being very important for verification).
+ */
+static bool rewriteEmptyDirectInvoke(Method* method, u2* insns)
+{
+ ClassObject* clazz = method->clazz;
+ Method* calledMethod;
+ u2 methodIdx = insns[1];
+
+ calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
+ if (calledMethod == NULL) {
+ LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n",
+ methodIdx,
+ (int) (insns - method->insns), clazz->descriptor,
+ method->name);
+ return false;
+ }
+
+ /* TODO: verify that java.lang.Object() is actually empty! */
+ if (calledMethod->clazz == gDvm.classJavaLangObject &&
+ dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
+ {
+ /*
+ * Replace with "empty" instruction. DO NOT disturb anything
+ * else about it, as we want it to function the same as
+ * OP_INVOKE_DIRECT when debugging is enabled.
+ */
+ assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
+ insns[0] = (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY;
+
+ //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
+ // method->clazz->descriptor, method->name,
+ // calledMethod->clazz->descriptor, calledMethod->name);
+ }
+
+ return true;
+}
+
+/*
+ * Resolve an interface method reference.
+ *
+ * No method access check here -- interface methods are always public.
+ *
+ * Returns NULL if the method was not found. Does not throw an exception.
+ */
+Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ Method* resMethod;
+ int i;
+
+ LOGVV("--- resolving interface method %d (referrer=%s)\n",
+ methodIdx, referrer->descriptor);
+
+ resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+ if (resMethod == NULL) {
+ const DexMethodId* pMethodId;
+ ClassObject* resClass;
+
+ pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+ resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
+ if (resClass == NULL) {
+ /* can't find the class that the method is a part of */
+ dvmClearOptException(dvmThreadSelf());
+ return NULL;
+ }
+ if (!dvmIsInterfaceClass(resClass)) {
+ /* whoops */
+ LOGI("Interface method not part of interface class\n");
+ return NULL;
+ }
+
+ const char* methodName =
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+ DexProto proto;
+ dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+ LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
+ methodName, methodSig, resClass->descriptor);
+ resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
+ if (resMethod == NULL) {
+ /* scan superinterfaces and superclass interfaces */
+ LOGVV("+++ did not resolve immediately\n");
+ for (i = 0; i < resClass->iftableCount; i++) {
+ resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
+ methodName, &proto);
+ if (resMethod != NULL)
+ break;
+ }
+
+ if (resMethod == NULL) {
+ LOGVV("+++ unable to resolve method %s\n", methodName);
+ return NULL;
+ }
+ } else {
+ LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
+ resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
+ }
+
+ /* we're expecting this to be abstract */
+ if (!dvmIsAbstractMethod(resMethod)) {
+ char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+ LOGW("Found non-abstract interface method %s.%s %s\n",
+ resMethod->clazz->descriptor, resMethod->name, desc);
+ free(desc);
+ return NULL;
+ }
+
+ /*
+ * Add it to the resolved table so we're faster on the next lookup.
+ */
+ dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+ }
+
+ LOGVV("--- found interface method %d (%s.%s)\n",
+ methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+ /* interface methods are always public; no need to check access */
+
+ return resMethod;
+}
+
+/*
+ * See if the method being called can be rewritten as an inline operation.
+ * Works for invoke-virtual, invoke-direct, and invoke-static.
+ *
+ * Returns "true" if we replace it.
+ */
+static bool rewriteExecuteInline(Method* method, u2* insns,
+ MethodType methodType, const InlineSub* inlineSubs)
+{
+ ClassObject* clazz = method->clazz;
+ Method* calledMethod;
+ u2 methodIdx = insns[1];
+
+ //return false;
+
+ calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
+ if (calledMethod == NULL) {
+ LOGV("+++ DexOpt inline: can't find %d\n", methodIdx);
+ return false;
+ }
+
+ while (inlineSubs->method != NULL) {
+ /*
+ if (extra) {
+ LOGI("comparing %p vs %p %s.%s %s\n",
+ inlineSubs->method, calledMethod,
+ inlineSubs->method->clazz->descriptor,
+ inlineSubs->method->name,
+ inlineSubs->method->signature);
+ }
+ */
+ if (inlineSubs->method == calledMethod) {
+ assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
+ (insns[0] & 0xff) == OP_INVOKE_STATIC ||
+ (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
+ insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE;
+ insns[1] = (u2) inlineSubs->inlineIdx;
+
+ //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
+ // method->clazz->descriptor, method->name,
+ // calledMethod->clazz->descriptor, calledMethod->name);
+ return true;
+ }
+
+ inlineSubs++;
+ }
+
+ return false;
+}
+
+/*
+ * See if the method being called can be rewritten as an inline operation.
+ * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range.
+ *
+ * Returns "true" if we replace it.
+ */
+static bool rewriteExecuteInlineRange(Method* method, u2* insns,
+ MethodType methodType, const InlineSub* inlineSubs)
+{
+ ClassObject* clazz = method->clazz;
+ Method* calledMethod;
+ u2 methodIdx = insns[1];
+
+ calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
+ if (calledMethod == NULL) {
+ LOGV("+++ DexOpt inline/range: can't find %d\n", methodIdx);
+ return false;
+ }
+
+ while (inlineSubs->method != NULL) {
+ if (inlineSubs->method == calledMethod) {
+ assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
+ (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
+ (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
+ insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE;
+ insns[1] = (u2) inlineSubs->inlineIdx;
+
+ //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
+ // method->clazz->descriptor, method->name,
+ // calledMethod->clazz->descriptor, calledMethod->name);
+ return true;
+ }
+
+ inlineSubs++;
+ }
+
+ return false;
+}
+
diff --git a/vm/analysis/Optimize.h b/vm/analysis/Optimize.h
new file mode 100644
index 0000000..eb47793
--- /dev/null
+++ b/vm/analysis/Optimize.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Bytecode optimization declarations.
+ */
+#ifndef _DALVIK_OPTIMIZE
+#define _DALVIK_OPTIMIZE
+
+/*
+ * Entry point from DEX preparation.
+ */
+bool dvmRewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
+ u4* pHeaderFlags, DexClassLookup** ppClassLookup);
+
+/*
+ * Abbreviated resolution functions, for use by optimization and verification
+ * code.
+ */
+ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
+ VerifyError* pFailure);
+Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
+ MethodType methodType, VerifyError* pFailure);
+Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx);
+InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
+ VerifyError* pFailure);
+StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
+ VerifyError* pFailure);
+
+#endif /*_DALVIK_OPTIMIZE*/
diff --git a/vm/analysis/VerifySubs.h b/vm/analysis/VerifySubs.h
index a87c6f1..9e05e9d 100644
--- a/vm/analysis/VerifySubs.h
+++ b/vm/analysis/VerifySubs.h
@@ -22,11 +22,13 @@
/*
* InsnFlags is a 32-bit integer with the following layout:
- * 0-15 instruction length (or 0 if this address doesn't hold an opcode)
- * 16 opcode flag (indicating this address holds an opcode)
- * 17 try block (indicating exceptions thrown here may be caught locally)
- * 30 visited (verifier has examined this instruction at least once)
- * 31 changed (set/cleared as bytecode verifier runs)
+ * 0-15 instruction length (or 0 if this address doesn't hold an opcode)
+ * 16-31 single bit flags:
+ * InTry: in "try" block; exceptions thrown here may be caught locally
+ * BranchTarget: other instructions can branch to this instruction
+ * GcPoint: this instruction is a GC safe point
+ * Visited: verifier has examined this instruction at least once
+ * Changed: set/cleared as bytecode verifier runs
*/
typedef u4 InsnFlags;