text: /*
   * 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 <string>
  
  #include <libgen.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <sys/mman.h>
  #include <sys/stat.h>
  #include <sys/file.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  #include <fcntl.h>
  #include <errno.h>
  #include <unistd.h>
  #include <zlib.h>
  
  /* fwd */
  static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
      DexClassLookup** ppClassLookup, DvmDex** ppDvmDex);
  static bool loadAllClasses(DvmDex* pDvmDex);
  static void verifyAndOptimizeClasses(DexFile* pDexFile, bool doVerify,
      bool doOpt);
  static void verifyAndOptimizeClass(DexFile* pDexFile, ClassObject* clazz,
      const DexClassDef* pClassDef, bool doVerify, bool doOpt);
  static void updateChecksum(u1* addr, int len, DexHeader* pHeader);
  static int writeDependencies(int fd, u4 modWhen, u4 crc);
  static bool writeOptData(int fd, const DexClassLookup* pClassLookup,\
      const RegisterMapBuilder* pRegMapBuilder);
  static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum);
  
  /*
   * Get just the directory portion of the given path. Equivalent to dirname(3).
   */
  static std::string saneDirName(const std::string& path) {
      size_t n = path.rfind('/');
      if (n == std::string::npos) {
          return ".";
      }
      return path.substr(0, n);
  }
  
  /*
   * Helper for dvmOpenCacheDexFile() in a known-error case: Check to
   * see if the directory part of the given path (all but the last
   * component) exists and is writable. Complain to the log if not.
   */
  static bool directoryIsValid(const std::string& fileName)
  {
      std::string dirName(saneDirName(fileName));
  
      struct stat sb;
      if (stat(dirName.c_str(), &sb) < 0) {
          ALOGE("Could not stat dex cache directory '%s': %s", dirName.c_str(), strerror(errno));
          return false;
      }
  
      if (!S_ISDIR(sb.st_mode)) {
          ALOGE("Dex cache directory isn't a directory: %s", dirName.c_str());
          return false;
      }
  
      if (access(dirName.c_str(), W_OK) < 0) {
          ALOGE("Dex cache directory isn't writable: %s", dirName.c_str());
          return false;
      }
  
      if (access(dirName.c_str(), R_OK) < 0) {
          ALOGE("Dex cache directory isn't readable: %s", dirName.c_str());
          return false;
      }
  
      return true;
  }
  
  /*
   * 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) {
                  // TODO: write an equivalent of strerror_r that returns a std::string.
                  const std::string errnoString(strerror(errno));
                  if (directoryIsValid(cacheFileName)) {
                      ALOGE("Can't open dex cache file '%s': %s", cacheFileName, errnoString.c_str());
                  }
              }
              return fd;
          }
          readOnly = true;
      } else {
          fchmod(fd, 0644);
      }
  
      /*
       * 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.
       */
      ALOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)",
          cacheFileName, fd, isBootstrap);
      ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
      cc = flock(fd, LOCK_EX | LOCK_NB);
      if (cc != 0) {
          ALOGD("DexOpt: sleeping on flock(%s)", cacheFileName);
          cc = flock(fd, LOCK_EX);
      }
      dvmChangeStatus(NULL, oldStatus);
      if (cc != 0) {
          ALOGE("Can't lock dex cache '%s': %d", cacheFileName, cc);
          close(fd);
          return -1;
      }
      ALOGV("DexOpt:  locked cache file");
  
      /*
       * 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) {
          ALOGE("Can't stat open file '%s'", cacheFileName);
          LOGVV("DexOpt: unlocking cache file %s", cacheFileName);
          goto close_fail;
      }
      cc = stat(cacheFileName, &fileStat);
      if (cc != 0 ||
          fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
      {
          ALOGD("DexOpt: our open cache file is stale; sleeping and retrying");
          LOGVV("DexOpt: unlocking cache file %s", 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) {
              ALOGW("DexOpt: file has zero length and isn't writable");
              goto close_fail;
          }
          cc = dexOptCreateEmptyHeader(fd);
          if (cc != 0)
              goto close_fail;
          *pNewFile = true;
          ALOGV("DexOpt: successfully initialized new cache file");
      } 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 ||
                     gDvm.dexOptMode == OPTIMIZE_MODE_FULL) {
              expectOpt = expectVerify;
          } else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/ {
              expectOpt = true;
          }
  
          ALOGV("checking deps, expecting vfy=%d opt=%d",
              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) {
                      ALOGW("Cached DEX '%s' (%s) is stale and not writable",
                          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.
               */
              ALOGD("ODEX file is stale or bad; removing and retrying (%s)",
                  cacheFileName);
              if (ftruncate(fd, 0) != 0) {
                  ALOGW("Warning: unable to truncate cache file '%s': %s",
                      cacheFileName, strerror(errno));
                  /* keep going */
              }
              if (unlink(cacheFileName) != 0) {
                  ALOGW("Warning: unable to remove cache file '%s': %d %s",
                      cacheFileName, errno, strerror(errno));
                  /* keep going; permission failure should probably be fatal */
              }
              LOGVV("DexOpt: unlocking cache file %s", cacheFileName);
              flock(fd, LOCK_UN);
              close(fd);
              goto retry;
          } else {
              ALOGV("DexOpt: good deps in cache file");
          }
      }
  
      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", 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;
  
      ALOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---", lastPart, isBootstrap);
  
      pid_t pid;
  
      /*
       * This could happen if something in our bootclasspath, which we thought
       * was all optimized, got rejected.
       */
      if (gDvm.optimizing) {
          ALOGW("Rejecting recursive optimization attempt on '%s'", 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);
          const char* argv[argc+1];             // last entry is NULL
          char values[argc][kMaxIntLen];
          char* execFile;
          const 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) {
              ALOGW("ANDROID_ROOT not set, defaulting to /system");
              androidRoot = "/system";
          }
          execFile = (char*)alloca(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_MAPS;
          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, const_cast<char**>(argv));
          else
              execv(execFile, const_cast<char**>(argv));
  
          ALOGE("execv '%s'%s failed: %s", execFile,
              kUseValgrind ? " [valgrind]" : "", strerror(errno));
          exit(1);
      } else {
          ALOGV("DexOpt: waiting for verify+opt, pid=%d", (int) pid);
          int status;
          pid_t gotPid;
  
          /*
           * Wait for the optimization process to finish.  We go into VMWAIT
           * mode here so GC suspension won't have to wait for us.
           */
          ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
          while (true) {
              gotPid = waitpid(pid, &status, 0);
              if (gotPid == -1 && errno == EINTR) {
                  ALOGD("waitpid interrupted, retrying");
              } else {
                  break;
              }
          }
          dvmChangeStatus(NULL, oldStatus);
          if (gotPid != pid) {
              ALOGE("waitpid failed: wanted %d, got %d: %s",
                  (int) pid, (int) gotPid, strerror(errno));
              return false;
          }
  
          if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
              ALOGD("DexOpt: --- END '%s' (success) ---", lastPart);
              return true;
          } else {
              ALOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed",
                  lastPart, status);
              return false;
          }
      }
  }
  
  /*
   * Do the actual optimization.  This is executed in the dexopt process.
   *
   * 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;
      RegisterMapBuilder* pRegMapBuilder = NULL;
  
      assert(gDvm.optimizing);
  
      ALOGV("Continuing optimization (%s, isb=%d)", fileName, isBootstrap);
  
      assert(dexOffset >= 0);
  
      /* quick test so we don't blow up on empty file */
      if (dexLength < (int) sizeof(DexHeader)) {
          ALOGE("too small to be DEX");
          return false;
      }
      if (dexOffset < (int) sizeof(DexOptHeader)) {
          ALOGE("not enough room for opt header");
          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) {
              ALOGE("unable to mmap DEX cache: %s", strerror(errno));
              goto bail;
          }
  
          bool doVerify, doOpt;
          if (gDvm.classVerifyMode == VERIFY_MODE_NONE) {
              doVerify = false;
          } else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE) {
              doVerify = !gDvm.optimizingBootstrapClass;
          } else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/ {
              doVerify = true;
          }
  
          if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE) {
              doOpt = false;
          } else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED ||
                     gDvm.dexOptMode == OPTIMIZE_MODE_FULL) {
              doOpt = doVerify;
          } else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/ {
              doOpt = true;
          }
  
          /*
           * 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 creates the class lookup table as part of doing the processing.
           */
          success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
                      doVerify, doOpt, &pClassLookup, NULL);
  
          if (success) {
              DvmDex* pDvmDex = NULL;
              u1* dexAddr = ((u1*) mapAddr) + dexOffset;
  
              if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
                  ALOGE("Unable to create DexFile");
                  success = false;
              } else {
                  /*
                   * If configured to do so, generate register map output
                   * for all verified classes.  The register maps were
                   * generated during verification, and will now be serialized.
                   */
                  if (gDvm.generateRegisterMaps) {
                      pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);
                      if (pRegMapBuilder == NULL) {
                          ALOGE("Failed generating register maps");
                          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) {
              ALOGW("msync failed: %s", 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) {
              ALOGE("munmap failed: %s", strerror(errno));
              goto bail;
          }
  #endif
  
          if (!success)
              goto bail;
      }
  
      /* get start offset, and adjust deps start for 64-bit alignment */
      off_t depsOffset, optOffset, endOffset, adjOffset;
      int depsLength, optLength;
      u4 optChecksum;
  
      depsOffset = lseek(fd, 0, SEEK_END);
      if (depsOffset < 0) {
          ALOGE("lseek to EOF failed: %s", strerror(errno));
          goto bail;
      }
      adjOffset = (depsOffset + 7) & ~(0x07);
      if (adjOffset != depsOffset) {
          ALOGV("Adjusting deps start from %d to %d",
              (int) depsOffset, (int) adjOffset);
          depsOffset = adjOffset;
          lseek(fd, depsOffset, SEEK_SET);
      }
  
      /*
       * Append the dependency list.
       */
      if (writeDependencies(fd, modWhen, crc) != 0) {
          ALOGW("Failed writing dependencies");
          goto bail;
      }
  
      /* compute deps length, then adjust opt start for 64-bit alignment */
      optOffset = lseek(fd, 0, SEEK_END);
      depsLength = optOffset - depsOffset;
  
      adjOffset = (optOffset + 7) & ~(0x07);
      if (adjOffset != optOffset) {
          ALOGV("Adjusting opt start from %d to %d",
              (int) optOffset, (int) adjOffset);
          optOffset = adjOffset;
          lseek(fd, optOffset, SEEK_SET);
      }
  
      /*
       * Append any optimized pre-computed data structures.
       */
      if (!writeOptData(fd, pClassLookup, pRegMapBuilder)) {
          ALOGW("Failed writing opt data");
          goto bail;
      }
  
      endOffset = lseek(fd, 0, SEEK_END);
      optLength = endOffset - optOffset;
  
      /* compute checksum from start of deps to end of opt area */
      if (!computeFileChecksum(fd, depsOffset,
              (optOffset+optLength) - 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.optOffset = (u4) optOffset;
      optHdr.optLength = (u4) optLength;
  #if __BYTE_ORDER != __LITTLE_ENDIAN
      optHdr.flags = DEX_OPT_FLAG_BIG;
  #else
      optHdr.flags = 0;
  #endif
      optHdr.checksum = optChecksum;
  
      fsync(fd);      /* ensure previous writes go before header is written */
  
      lseek(fd, 0, SEEK_SET);
      if (sysWriteFully(fd, &optHdr, sizeof(optHdr), "DexOpt opt header") != 0)
          goto bail;
  
      ALOGV("Successfully wrote DEX header");
      result = true;
  
      //dvmRegisterMapDumpStats();
  
  bail:
      dvmFreeRegisterMapBuilder(pRegMapBuilder);
      free(pClassLookup);
      return result;
  }
  
  /*
   * Prepare an in-memory DEX file.
   *
   * The data was presented to the VM as a byte array rather than a file.
   * We want to do the same basic set of operations, but we can just leave
   * them in memory instead of writing them out to a cached optimized DEX file.
   */
  bool dvmPrepareDexInMemory(u1* addr, size_t len, DvmDex** ppDvmDex)
  {
      DexClassLookup* pClassLookup = NULL;
  
      /*
       * Byte-swap, realign, verify basic DEX file structure.
       *
       * We could load + verify + optimize here as well, but that's probably
       * not desirable.
       *
       * (The bulk-verification code is currently only setting the DEX
       * file's "verified" flag, not updating the ClassObject.  This would
       * also need to be changed, or we will try to verify the class twice,
       * and possibly reject it when optimized opcodes are encountered.)
       */
      if (!rewriteDex(addr, len, false, false, &pClassLookup, ppDvmDex)) {
          return false;
      }
  
      (*ppDvmDex)->pDexFile->pClassLookup = pClassLookup;
  
      return true;
  }
  
  /*
   * Perform in-place rewrites on a memory-mapped DEX file.
   *
   * If this is called from a short-lived child process (dexopt), we can
   * go nutty with loading classes and allocating memory.  When it's
   * called to prepare classes provided in a byte array, we may want to
   * be more conservative.
   *
   * If "ppClassLookup" is non-NULL, a pointer to a newly-allocated
   * DexClassLookup will be returned on success.
   *
   * If "ppDvmDex" is non-NULL, a newly-allocated DvmDex struct will be
   * returned on success.
   */
  static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
      DexClassLookup** ppClassLookup, DvmDex** ppDvmDex)
  {
      DexClassLookup* pClassLookup = NULL;
      u8 prepWhen, loadWhen, verifyOptWhen;
      DvmDex* pDvmDex = NULL;
      bool result = false;
      const char* msgStr = "???";
  
      /* if the DEX is in the wrong byte order, swap it now */
      if (dexSwapAndVerify(addr, len) != 0)
          goto bail;
  
      /*
       * Now that the DEX file can be read directly, create a DexFile struct
       * for it.
       */
      if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
          ALOGE("Unable to create DexFile");
          goto bail;
      }
  
      /*
       * Create the class lookup table.  This will eventually be appended
       * to the end of the .odex.
       *
       * We create a temporary link from the DexFile for the benefit of
       * class loading, below.
       */
      pClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
      if (pClassLookup == NULL)
          goto bail;
      pDvmDex->pDexFile->pClassLookup = pClassLookup;
  
      /*
       * If we're not going to attempt to verify or optimize the classes,
       * there's no value in loading them, so bail out early.
       */
      if (!doVerify && !doOpt) {
          result = true;
          goto bail;
      }
  
      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();
  
      /*
       * Create a data structure for use by the bytecode optimizer.
       * We need to look up methods in a few classes, so this may cause
       * a bit of class loading.  We usually do this during VM init, but
       * for dexopt on core.jar the order of operations gets a bit tricky,
       * so we defer it to here.
       */
      if (!dvmCreateInlineSubsTable())
          goto bail;
  
      /*
       * Verify and optimize all classes in the DEX file (command-line
       * options permitting).
       *
       * This is best-effort, so there's really no way for dexopt to
       * fail at this point.
       */
      verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt);
      verifyOptWhen = dvmGetRelativeTimeUsec();
  
      if (doVerify && doOpt)
          msgStr = "verify+opt";
      else if (doVerify)
          msgStr = "verify";
      else if (doOpt)
          msgStr = "opt";
      ALOGD("DexOpt: load %dms, %s %dms, %d bytes",
          (int) (loadWhen - prepWhen) / 1000,
          msgStr,
          (int) (verifyOptWhen - loadWhen) / 1000,
          gDvm.pBootLoaderAlloc->curOffset);
  
      result = true;
  
  bail:
      /*
       * On success, return the pieces that the caller asked for.
       */
  
      if (pDvmDex != NULL) {
          /* break link between the two */
          pDvmDex->pDexFile->pClassLookup = NULL;
      }
  
      if (ppDvmDex == NULL || !result) {
          dvmDexFileFree(pDvmDex);
      } else {
          *ppDvmDex = pDvmDex;
      }
  
      if (ppClassLookup == NULL || !result) {
          free(pClassLookup);
      } else {
          *ppClassLookup = pClassLookup;
      }
  
      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;
  
      ALOGV("DexOpt: +++ trying to load %d classes", count);
  
      dvmSetBootPathExtraDex(pDvmDex);
  
      /*
       * At this point, it is safe -- and necessary! -- to look up the
       * VM's required classes and members, even when what we are in the
       * process of processing is the core library that defines these
       * classes itself. (The reason it is necessary is that in the act
       * of initializing the class Class, below, the system will end up
       * referring to many of the class references that got set up by
       * this call.)
       */
      if (!dvmFindRequiredClassesAndMembers()) {
          return false;
      }
  
      /*
       * 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-and-initialize. The call to
       * dvmFindSystemClass() here takes care of that situation. (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 (!dvmInitClass(gDvm.classJavaLangClass)) {
          ALOGE("ERROR: failed to initialize the class Class!");
          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);
  
          ALOGV("+++  loading '%s'", classDescriptor);
          //newClass = dvmDefineClass(pDexFile, classDescriptor,
          //        NULL);
          newClass = dvmFindSystemClassNoInit(classDescriptor);
          if (newClass == NULL) {
              ALOGV("DexOpt: failed loading '%s'", 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.
               */
              ALOGD("DexOpt: '%s' has an earlier definition; blocking out",
                  classDescriptor);
              SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
          } else {
              loaded++;
          }
      }
      ALOGV("DexOpt: +++ successfully loaded %d classes", loaded);
  
      dvmSetBootPathExtraDex(NULL);
      return true;
  }
  
  /*
   * Verify and/or optimize all classes that were successfully loaded from
   * this DEX file.
   */
  static void verifyAndOptimizeClasses(DexFile* pDexFile, bool doVerify,
      bool doOpt)
  {
      u4 count = pDexFile->pHeader->classDefsSize;
      u4 idx;
  
      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) {
              verifyAndOptimizeClass(pDexFile, clazz, pClassDef, doVerify, doOpt);
  
          } else {
              // TODO: log when in verbose mode
              ALOGV("DexOpt: not optimizing unavailable class '%s'",
                  classDescriptor);
          }
      }
  
  #ifdef VERIFIER_STATS
      ALOGI("Verifier stats:");
      ALOGI(" methods examined        : %u", gDvm.verifierStats.methodsExamined);
      ALOGI(" monitor-enter methods   : %u", gDvm.verifierStats.monEnterMethods);
      ALOGI(" instructions examined   : %u", gDvm.verifierStats.instrsExamined);
      ALOGI(" instructions re-examined: %u", gDvm.verifierStats.instrsReexamined);
      ALOGI(" copying of register sets: %u", gDvm.verifierStats.copyRegCount);
      ALOGI(" merging of register sets: %u", gDvm.verifierStats.mergeRegCount);
      ALOGI(" ...that caused changes  : %u", gDvm.verifierStats.mergeRegChanged);
      ALOGI(" uninit searches         : %u", gDvm.verifierStats.uninitSearches);
      ALOGI(" max memory required     : %u", gDvm.verifierStats.biggestAlloc);
  #endif
  }
  
  /*
   * Verify and/or optimize a specific class.
   */
  static void verifyAndOptimizeClass(DexFile* pDexFile, ClassObject* clazz,
      const DexClassDef* pClassDef, bool doVerify, bool doOpt)
  {
  #ifndef LOG_NDEBUG
      const char* classDescriptor;
  #endif
      bool verified = false;
  
      if (clazz->pDvmDex->pDexFile != pDexFile) {
          /*
           * The current DEX file defined a class that is also present in the
           * bootstrap class path.  The class loader favored the bootstrap
           * version, which means that we have a pointer to a class that is
           * (a) not the one we want to examine, and (b) mapped read-only,
           * so we will seg fault if we try to rewrite instructions inside it.
           */
          ALOGD("DexOpt: not verifying/optimizing '%s': multiple definitions",
              clazz->descriptor);
          return;
      }
  
  #ifndef LOG_NDEBUG
      classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
  #endif
  
      /*
       * First, try to verify it.
       */
      if (doVerify) {
          if (dvmVerifyClass(clazz)) {
              /*
               * Set the "is preverified" flag in the DexClassDef.  We
               * do it here, rather than in the ClassObject structure,
               * because the DexClassDef is part of the odex file.
               */
              assert((clazz->accessFlags & JAVA_FLAGS_MASK) ==
                  pClassDef->accessFlags);
              ((DexClassDef*)pClassDef)->accessFlags |= CLASS_ISPREVERIFIED;
              verified = true;
          } else {
              // TODO: log when in verbose mode
              ALOGV("DexOpt: '%s' failed verification", classDescriptor);
          }
      }
  
      if (doOpt) {
          bool needVerify = (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED ||
                             gDvm.dexOptMode == OPTIMIZE_MODE_FULL);
          if (!verified && needVerify) {
              ALOGV("DexOpt: not optimizing '%s': not verified",
                  classDescriptor);
          } else {
              dvmOptimizeClass(clazz, false);
  
              /* set the flag whether or not we actually changed anything */
              ((DexClassDef*)pClassDef)->accessFlags |= CLASS_ISOPTIMIZED;
          }
      }
  }
  
  
  /*
   * 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:
          ALOGE("DexOpt: unexpected cpe kind %d", 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:
          ALOGE("unexpected cpe kind %d", 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 + 2048;     // 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) {
          ALOGE("DexOpt: failed to seek to start of file: %s", strerror(errno));
          goto bail;
      }
  
      /*
       * Read and do trivial verification on the opt header.  The header is
       * always in host byte order.
       */
      actual = read(fd, &optHdr, sizeof(optHdr));
      if (actual < 0) {
          ALOGE("DexOpt: failed reading opt header: %s", strerror(errno));
          goto bail;
      } else if (actual != sizeof(optHdr)) {
          ALOGE("DexOpt: failed reading opt header (got %d of %zd)",
              (int) actual, sizeof(optHdr));
          goto bail;
      }
  
      magic = optHdr.magic;
      if (memcmp(magic, DEX_MAGIC, 4) == 0) {
          /* somebody probably pointed us at the wrong file */
          ALOGD("DexOpt: expected optimized DEX, found unoptimized");
          goto bail;
      } else if (memcmp(magic, DEX_OPT_MAGIC, 4) != 0) {
          /* not a DEX file, or previous attempt was interrupted */
          ALOGD("DexOpt: incorrect opt magic number (0x%02x %02x %02x %02x)",
              magic[0], magic[1], magic[2], magic[3]);
          goto bail;
      }
      if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
          ALOGW("DexOpt: stale opt version (0x%02x %02x %02x %02x)",
              magic[4], magic[5], magic[6], magic[7]);
          goto bail;
      }
      if (optHdr.depsLength < kMinDepSize || optHdr.depsLength > kMaxDepSize) {
          ALOGW("DexOpt: weird deps length %d, bailing", optHdr.depsLength);
          goto bail;
      }
  
      /*
       * Do the header flags match up with what we want?
       *
       * 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 ((expectedFlags & matchMask) != (optHdr.flags & matchMask)) {
              ALOGI("DexOpt: header flag mismatch (0x%02x vs 0x%02x, mask=0x%02x)",
                  expectedFlags, optHdr.flags, matchMask);
              goto bail;
          }
      }
  
      posn = lseek(fd, optHdr.depsOffset, SEEK_SET);
      if (posn < 0) {
          ALOGW("DexOpt: seek to deps failed: %s", strerror(errno));
          goto bail;
      }
  
      /*
       * Read all of the dependency stuff into memory.
       */
      depData = (u1*) malloc(optHdr.depsLength);
      if (depData == NULL) {
          ALOGW("DexOpt: unable to allocate %d bytes for deps",
              optHdr.depsLength);
          goto bail;
      }
      actual = read(fd, depData, optHdr.depsLength);
      if (actual < 0) {
          ALOGW("DexOpt: failed reading deps: %s", strerror(errno));
          goto bail;
      } else if (actual != (ssize_t) optHdr.depsLength) {
          ALOGW("DexOpt: failed reading deps: got %d of %d",
              (int) actual, optHdr.depsLength);
          goto bail;
      }
  
      /*
       * Verify simple items.
       */
      const u1* ptr;
      u4 val;
  
      ptr = depData;
      val = read4LE(&ptr);
      if (sourceAvail && val != modWhen) {
          ALOGI("DexOpt: source file mod time mismatch (%08x vs %08x)",
              val, modWhen);
          goto bail;
      }
      val = read4LE(&ptr);
      if (sourceAvail && val != crc) {
          ALOGI("DexOpt: source file CRC mismatch (%08x vs %08x)", val, crc);
          goto bail;
      }
      val = read4LE(&ptr);
      if (val != DALVIK_VM_BUILD) {
          ALOGD("DexOpt: VM build version mismatch (%d vs %d)",
              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);
      ALOGV("+++ DexOpt: numDeps = %d", numDeps);
      for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
          const char* cacheFileName =
              dvmPathToAbsolutePortion(getCacheFileName(cpe));
          assert(cacheFileName != NULL); /* guaranteed by Class.c */
  
          const u1* signature = getSignature(cpe);
          size_t len = strlen(cacheFileName) +1;
          u4 storedStrLen;
  
          if (numDeps == 0) {
              /* more entries in bootclasspath than in deps list */
              ALOGI("DexOpt: not all deps represented");
              goto bail;
          }
  
          storedStrLen = read4LE(&ptr);
          if (len != storedStrLen ||
              strcmp(cacheFileName, (const char*) ptr) != 0)
          {
              ALOGI("DexOpt: mismatch dep name: '%s' vs. '%s'",
                  cacheFileName, ptr);
              goto bail;
          }
  
          ptr += storedStrLen;
  
          if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
              ALOGI("DexOpt: mismatch dep signature for '%s'", cacheFileName);
              goto bail;
          }
          ptr += kSHA1DigestLen;
  
          ALOGV("DexOpt: dep match on '%s'", cacheFileName);
  
          numDeps--;
      }
  
      if (numDeps != 0) {
          /* more entries in deps list than in classpath */
          ALOGI("DexOpt: Some deps went away");
          goto bail;
      }
  
      // consumed all data and no more?
      if (ptr != depData + optHdr.depsLength) {
          ALOGW("DexOpt: Spurious dep data? %d vs %d",
              (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;
      int result = -1;
      ssize_t bufLen;
      ClassPathEntry* cpe;
      int 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 =
              dvmPathToAbsolutePortion(getCacheFileName(cpe));
          assert(cacheFileName != NULL); /* guaranteed by Class.c */
  
          ALOGV("+++ DexOpt: found dep '%s'", cacheFileName);
  
          numDeps++;
          bufLen += strlen(cacheFileName) +1;
      }
  
      bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
  
      buf = (u1*)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 =
              dvmPathToAbsolutePortion(getCacheFileName(cpe));
          assert(cacheFileName != NULL); /* guaranteed by Class.c */
  
          const u1* signature = getSignature(cpe);
          int len = strlen(cacheFileName) +1;
  
          if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
              ALOGE("DexOpt: overran buffer");
              dvmAbort();
          }
  
          set4LE(ptr, len);
          ptr += 4;
          memcpy(ptr, cacheFileName, len);
          ptr += len;
          memcpy(ptr, signature, kSHA1DigestLen);
          ptr += kSHA1DigestLen;
      }
  
      assert(ptr == buf + bufLen);
  
      result = sysWriteFully(fd, buf, bufLen, "DexOpt dep info");
  
      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)
  {
      union {             /* save a syscall by grouping these together */
          char raw[8];
          struct {
              u4 type;
              u4 size;
          } ts;
      } header;
  
      assert(sizeof(header) == 8);
  
      ALOGV("Writing chunk, type=%.4s size=%d", (char*) &type, size);
  
      header.ts.type = type;
      header.ts.size = (u4) size;
      if (sysWriteFully(fd, &header, sizeof(header),
              "DexOpt opt chunk header write") != 0)
      {
          return false;
      }
  
      if (size > 0) {
          if (sysWriteFully(fd, data, size, "DexOpt opt chunk write") != 0)
              return false;
      }
  
      /* if necessary, pad to 64-bit alignment */
      if ((size & 7) != 0) {
          int padSize = 8 - (size & 7);
          ALOGV("size was %d, inserting %d pad bytes", size, padSize);
          lseek(fd, padSize, SEEK_CUR);
      }
  
      assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
  
      return true;
  }
  
  /*
   * Write opt 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 writeOptData(int fd, const DexClassLookup* pClassLookup,
      const RegisterMapBuilder* pRegMapBuilder)
  {
      /* pre-computed class lookup hash table */
      if (!writeChunk(fd, (u4) kDexChunkClassLookup,
              pClassLookup, pClassLookup->size))
      {
          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;
  }
  
  /*
   * 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) {
          ALOGE("Unable to seek to start of checksum area (%ld): %s",
              (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) {
              ALOGE("Read failed (%d) while computing checksum (len=%zu): %s",
                  (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 optimized 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;
  }
  