/* 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 */
/* tuck this into the DexFile so it gets released later */
sysCopyMap(&pDvmDex->memMap, &memMap);
+ pDvmDex->isMappedReadOnly = true;
*ppDvmDex = pDvmDex;
result = 0;
goto bail;
}
+ pDvmDex->isMappedReadOnly = false;
*ppDvmDex = pDvmDex;
result = 0;
struct AtomicCache* pInterfaceCache;
/* shared memory region with file contents */
+ bool isMappedReadOnly;
MemMapping memMap;
/* lock ensuring mutual exclusion during updates */
}
/* 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;
}
/*
* 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.
/* 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 */
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;
/* 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);
{
DexClassLookup* pClassLookup = NULL;
RegisterMapBuilder* pRegMapBuilder = NULL;
- u4 headerFlags = 0;
assert(gDvm.optimizing);
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
* 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;
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 */
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
/*
* 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,
goto bail;
}
- /* this is needed for the next part */
- pDvmDex->pDexFile->pClassLookup = *ppClassLookup;
-
prepWhen = dvmGetRelativeTimeUsec();
/*
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;
}
/*
* 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);
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);
}
/*
- * 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);
}
}
*/
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);
}
/*
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);
* 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,
*/
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);
(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,
(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,
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.
*/
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);
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))