Low-level support for in-memory DEX

We want to be able to load classes from a DEX file in memory,
rather than insisting that they always be loaded from disk.  This
provides the underpinnings.

The code was previously using the "are we in dexopt" flag to
decide if it needed to mprotect(RW) DEX data before altering it.
We now have an explicit flag.

Also, scraped off some "opt header flags" checks that never did much.

Bug 1338213

Change-Id: If7128bf246992156662e089a2a87cebf475a6f2a
diff --git a/libdex/DexFile.h b/libdex/DexFile.h
index 06dc864..fd865e2 100644
--- a/libdex/DexFile.h
+++ b/libdex/DexFile.h
@@ -464,10 +464,7 @@
     /* pad for 64-bit alignment if necessary */
 } DexOptHeader;
 
-#define DEX_FLAG_VERIFIED           (1)     /* tried to verify all classes */
 #define DEX_OPT_FLAG_BIG            (1<<1)  /* swapped to big-endian */
-#define DEX_OPT_FLAG_FIELDS         (1<<2)  /* field access optimized */
-#define DEX_OPT_FLAG_INVOCATIONS    (1<<3)  /* method calls optimized */
 
 #define DEX_INTERFACE_CACHE_SIZE    128     /* must be power of 2 */
 
diff --git a/vm/DvmDex.c b/vm/DvmDex.c
index 500bf42..c5790e5 100644
--- a/vm/DvmDex.c
+++ b/vm/DvmDex.c
@@ -137,6 +137,7 @@
 
     /* tuck this into the DexFile so it gets released later */
     sysCopyMap(&pDvmDex->memMap, &memMap);
+    pDvmDex->isMappedReadOnly = true;
     *ppDvmDex = pDvmDex;
     result = 0;
 
@@ -175,6 +176,7 @@
         goto bail;
     }
 
+    pDvmDex->isMappedReadOnly = false;
     *ppDvmDex = pDvmDex;
     result = 0;
 
diff --git a/vm/DvmDex.h b/vm/DvmDex.h
index f36940b..ad82e54 100644
--- a/vm/DvmDex.h
+++ b/vm/DvmDex.h
@@ -58,6 +58,7 @@
     struct AtomicCache* pInterfaceCache;
 
     /* shared memory region with file contents */
+    bool                isMappedReadOnly;
     MemMapping          memMap;
 
     /* lock ensuring mutual exclusion during updates */
diff --git a/vm/RawDexFile.c b/vm/RawDexFile.c
index 5da4907..2c73481 100644
--- a/vm/RawDexFile.c
+++ b/vm/RawDexFile.c
@@ -250,11 +250,20 @@
 }
 
 /* See documentation comment in header. */
-int dvmRawDexFileOpenArray(const u1* pBytes, u4 length,
-    RawDexFile** ppDexFile)
+int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppRawDexFile)
 {
-    // TODO - should be very similar to what JarFile does.
-    return -1;
+    DvmDex* pDvmDex = NULL;
+
+    if (!dvmPrepareDexInMemory(pBytes, length, &pDvmDex)) {
+        LOGD("Unable to open raw DEX from array\n");
+        return -1;
+    }
+    assert(pDvmDex != NULL);
+
+    *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
+    (*ppRawDexFile)->pDvmDex = pDvmDex;
+
+    return 0;
 }
 
 /*
diff --git a/vm/RawDexFile.h b/vm/RawDexFile.h
index f15aac2..cbcb3b6 100644
--- a/vm/RawDexFile.h
+++ b/vm/RawDexFile.h
@@ -49,8 +49,7 @@
  * On success, returns 0 and sets "*ppDexFile" to a newly-allocated DexFile.
  * On failure, returns a meaningful error code [currently just -1].
  */
-int dvmRawDexFileOpenArray(const u1* pBytes, u4 length,
-    RawDexFile** ppDexFile);
+int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppDexFile);
 
 /*
  * Free a RawDexFile structure, along with any associated structures.
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index 1fff44b..1894d5d 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -3306,10 +3306,10 @@
         /* nothing to do */
         break;
     case 3:
-        dvmDexChangeDex2(meth->clazz->pDvmDex, oldInsns+2, OP_NOP);
+        dvmUpdateCodeUnit(meth, oldInsns+2, OP_NOP);
         break;
     case 5:
-        dvmDexChangeDex2(meth->clazz->pDvmDex, oldInsns+4, OP_NOP);
+        dvmUpdateCodeUnit(meth, oldInsns+4, OP_NOP);
         break;
     default:
         /* whoops */
@@ -3324,15 +3324,15 @@
         assert(width == 4 || width == 5);
         u2 newVal = (u2) ((OP_THROW_VERIFICATION_ERROR_JUMBO << 8) |
                            OP_DISPATCH_FF);
-        dvmDexChangeDex2(meth->clazz->pDvmDex, oldInsns, newVal);
+        dvmUpdateCodeUnit(meth, oldInsns, newVal);
         newVal = failure | (refType << kVerifyErrorRefTypeShift);
-        dvmDexChangeDex2(meth->clazz->pDvmDex, oldInsns+3, newVal);
+        dvmUpdateCodeUnit(meth, oldInsns+3, newVal);
     } else {
         /* encode the opcode, with the failure code in the high byte */
         assert(width == 2 || width == 3);
         u2 newVal = OP_THROW_VERIFICATION_ERROR |
             (failure << 8) | (refType << (8 + kVerifyErrorRefTypeShift));
-        dvmDexChangeDex2(meth->clazz->pDvmDex, oldInsns, newVal);
+        dvmUpdateCodeUnit(meth, oldInsns, newVal);
     }
 
     result = true;
diff --git a/vm/analysis/DexPrepare.c b/vm/analysis/DexPrepare.c
index eb856e8..1e3eeaa 100644
--- a/vm/analysis/DexPrepare.c
+++ b/vm/analysis/DexPrepare.c
@@ -42,8 +42,8 @@
 
 
 /* fwd */
-static bool rewriteDex(u1* addr, int len, u4* pHeaderFlags,
-    DexClassLookup** ppClassLookup);
+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);
@@ -479,7 +479,6 @@
 {
     DexClassLookup* pClassLookup = NULL;
     RegisterMapBuilder* pRegMapBuilder = NULL;
-    u4 headerFlags = 0;
 
     assert(gDvm.optimizing);
 
@@ -521,6 +520,24 @@
             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
@@ -530,11 +547,10 @@
          * 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.
+         * This creates the class lookup table as part of doing the processing.
          */
         success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
-                    &headerFlags, &pClassLookup);
+                    doVerify, doOpt, &pClassLookup, NULL);
 
         if (success) {
             DvmDex* pDvmDex = NULL;
@@ -656,8 +672,11 @@
     optHdr.depsLength = (u4) depsLength;
     optHdr.optOffset = (u4) optOffset;
     optHdr.optLength = (u4) optLength;
-
-    optHdr.flags = headerFlags;
+#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 */
@@ -677,52 +696,62 @@
     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.
  *
- * This happens in a short-lived child process, so we can go nutty with
- * loading classes and allocating memory.
+ * 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, u4* pHeaderFlags,
-    DexClassLookup** ppClassLookup)
+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 doVerify, doOpt;
     bool result = false;
 
-    *pHeaderFlags = 0;
-
     /* if the DEX is in the wrong byte order, swap it now */
     if (dexSwapAndVerify(addr, len) != 0)
         goto bail;
-#if __BYTE_ORDER != __LITTLE_ENDIAN
-    *pHeaderFlags |= DEX_OPT_FLAG_BIG;
-#endif
-
-    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;
-    }
-
-    /* TODO: decide if this is actually useful */
-    if (doVerify)
-        *pHeaderFlags |= DEX_FLAG_VERIFIED;
-    if (doOpt)
-        *pHeaderFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
 
     /*
      * Now that the DEX file can be read directly, create a DexFile struct
@@ -736,10 +765,14 @@
     /*
      * 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.
      */
-    *ppClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
-    if (*ppClassLookup == NULL)
+    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,
@@ -750,9 +783,6 @@
         goto bail;
     }
 
-    /* this is needed for the next part */
-    pDvmDex->pDexFile->pClassLookup = *ppClassLookup;
-
     prepWhen = dvmGetRelativeTimeUsec();
 
     /*
@@ -798,8 +828,23 @@
     result = true;
 
 bail:
-    /* free up storage */
-    dvmDexFileFree(pDvmDex);
+    /*
+     * On success, return the pieces that the caller asked for.
+     */
+    if (ppDvmDex == NULL || !result) {
+        dvmDexFileFree(pDvmDex);
+    } else {
+        *ppDvmDex = pDvmDex;
+    }
+
+    if (ppClassLookup == NULL || !result) {
+        free(pClassLookup);
+    } else {
+        *ppClassLookup = pClassLookup;
+    }
+
+    /* break link between the two */
+    pDvmDex->pDexFile->pClassLookup = NULL;
 
     return result;
 }
@@ -1125,29 +1170,13 @@
     /*
      * 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.
+     * 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);
diff --git a/vm/analysis/DexPrepare.h b/vm/analysis/DexPrepare.h
index a424b9c..0ee76e5 100644
--- a/vm/analysis/DexPrepare.h
+++ b/vm/analysis/DexPrepare.h
@@ -124,6 +124,11 @@
     const char* fileName, u4 modWhen, u4 crc, bool isBootstrap);
 
 /*
+ * Prepare DEX data that is only available to the VM as in-memory data.
+ */
+bool dvmPrepareDexInMemory(u1* addr, size_t len, DvmDex** ppDvmDex);
+
+/*
  * Prep data structures.
  */
 bool dvmCreateInlineSubsTable(void);
diff --git a/vm/analysis/Optimize.c b/vm/analysis/Optimize.c
index ae6eebe..2bacc5b 100644
--- a/vm/analysis/Optimize.c
+++ b/vm/analysis/Optimize.c
@@ -338,16 +338,19 @@
 }
 
 /*
- * Update a 16-bit code unit in "meth".
+ * Update a 16-bit code unit in "meth".  The way in which the DEX data was
+ * loaded determines how we go about the write.
  */
-static inline void updateCodeUnit(const Method* meth, u2* ptr, u2 newVal)
+void dvmUpdateCodeUnit(const Method* meth, u2* ptr, u2 newVal)
 {
-    if (gDvm.optimizing) {
-        /* dexopt time, alter the output directly */
+    DvmDex* pDvmDex = meth->clazz->pDvmDex;
+
+    if (!pDvmDex->isMappedReadOnly) {
+        /* in-memory DEX (dexopt or byte[]), alter the output directly */
         *ptr = newVal;
     } else {
-        /* runtime, toggle the page read/write status */
-        dvmDexChangeDex2(meth->clazz->pDvmDex, ptr, newVal);
+        /* memory-mapped file, toggle the page read/write status */
+        dvmDexChangeDex2(pDvmDex, ptr, newVal);
     }
 }
 
@@ -356,7 +359,7 @@
  */
 static inline void updateOpcode(const Method* meth, u2* ptr, Opcode opcode)
 {
-    updateCodeUnit(meth, ptr, (ptr[0] & 0xff00) | (u2) opcode);
+    dvmUpdateCodeUnit(meth, ptr, (ptr[0] & 0xff00) | (u2) opcode);
 }
 
 /*
@@ -671,7 +674,7 @@
             instField->field.clazz->descriptor, instField->field.name);
     } else if (quickOpc != OP_NOP) {
         updateOpcode(method, insns, quickOpc);
-        updateCodeUnit(method, insns+1, (u2) instField->byteOffset);
+        dvmUpdateCodeUnit(method, insns+1, (u2) instField->byteOffset);
         LOGV("DexOpt: rewrote ifield access %s.%s --> %d\n",
             instField->field.clazz->descriptor, instField->field.name,
             instField->byteOffset);
@@ -885,7 +888,7 @@
      * initial load.
      */
     updateOpcode(method, insns, newOpc);
-    updateCodeUnit(method, insns+1, baseMethod->methodIndex);
+    dvmUpdateCodeUnit(method, insns+1, baseMethod->methodIndex);
 
     //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
     //    method->clazz->descriptor, method->name,
@@ -933,7 +936,8 @@
          */
         u1 origOp = insns[0] & 0xff;
         if (origOp == OP_INVOKE_DIRECT) {
-            updateCodeUnit(method, insns, OP_INVOKE_OBJECT_INIT_RANGE | 0x100);
+            dvmUpdateCodeUnit(method, insns,
+                OP_INVOKE_OBJECT_INIT_RANGE | 0x100);
         } else {
             assert(origOp == OP_INVOKE_DIRECT_RANGE);
             assert((insns[0] >> 8) == 1);
@@ -1053,7 +1057,7 @@
                    (insns[0] & 0xff) == OP_INVOKE_STATIC ||
                    (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
             updateOpcode(method, insns, OP_EXECUTE_INLINE);
-            updateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx);
+            dvmUpdateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx);
 
             //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
             //    method->clazz->descriptor, method->name,
@@ -1093,7 +1097,7 @@
                    (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
                    (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
             updateOpcode(method, insns, OP_EXECUTE_INLINE_RANGE);
-            updateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx);
+            dvmUpdateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx);
 
             //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
             //    method->clazz->descriptor, method->name,
diff --git a/vm/analysis/Optimize.h b/vm/analysis/Optimize.h
index 3b3ba7e..75b6eab 100644
--- a/vm/analysis/Optimize.h
+++ b/vm/analysis/Optimize.h
@@ -26,6 +26,11 @@
 void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly);
 
 /*
+ * Update a 16-bit code unit.
+ */
+void dvmUpdateCodeUnit(const Method* meth, u2* ptr, u2 newVal);
+
+/*
  * Abbreviated resolution functions, for use by optimization and verification
  * code.
  */
diff --git a/vm/native/dalvik_system_DexFile.c b/vm/native/dalvik_system_DexFile.c
index af7eb89..9c9a6e1 100644
--- a/vm/native/dalvik_system_DexFile.c
+++ b/vm/native/dalvik_system_DexFile.c
@@ -259,7 +259,7 @@
         RETURN_VOID();
     }
 
-    /* TODO: Avoid making a copy of the array. */
+    /* TODO: Avoid making a copy of the array. (note array *is* modified) */
     length = fileContentsObj->length;
     pBytes = (u1*) malloc(length);
 
@@ -360,7 +360,8 @@
 
     name = dvmCreateCstrFromString(nameObj);
     descriptor = dvmDotToDescriptor(name);
-    LOGV("--- Explicit class load '%s' 0x%08x\n", descriptor, cookie);
+    LOGV("--- Explicit class load '%s' l=%p c=0x%08x\n",
+        descriptor, loader, cookie);
     free(name);
 
     if (!validateCookie(cookie))