/*
* 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.
*/
/*
* VM-specific state associated with a DEX file.
*/
#include "Dalvik.h"
/*
* Create auxillary data structures.
*
* We need a 4-byte pointer for every reference to a class, method, field,
* or string constant. Summed up over all loaded DEX files (including the
* whoppers in the boostrap class path), this adds up to be quite a bit
* of native memory.
*
* For more traditional VMs these values could be stuffed into the loaded
* class file constant pool area, but we don't have that luxury since our
* classes are memory-mapped read-only.
*
* The DEX optimizer will remove the need for some of these (e.g. we won't
* use the entry for virtual methods that are only called through
* invoke-virtual-quick), creating the possibility of some space reduction
* at dexopt time.
*/
static DvmDex* allocateAuxStructures(DexFile* pDexFile)
{
DvmDex* pDvmDex;
const DexHeader* pHeader;
u4 stringCount, classCount, methodCount, fieldCount;
pDvmDex = (DvmDex*) calloc(1, sizeof(DvmDex));
if (pDvmDex == NULL)
return NULL;
pDvmDex->pDexFile = pDexFile;
pDvmDex->pHeader = pDexFile->pHeader;
pHeader = pDvmDex->pHeader;
stringCount = pHeader->stringIdsSize;
classCount = pHeader->typeIdsSize;
methodCount = pHeader->methodIdsSize;
fieldCount = pHeader->fieldIdsSize;
pDvmDex->pResStrings = (struct StringObject**)
calloc(stringCount, sizeof(struct StringObject*));
pDvmDex->pResClasses = (struct ClassObject**)
calloc(classCount, sizeof(struct ClassObject*));
pDvmDex->pResMethods = (struct Method**)
calloc(methodCount, sizeof(struct Method*));
pDvmDex->pResFields = (struct Field**)
calloc(fieldCount, sizeof(struct Field*));
LOGV("+++ DEX %p: allocateAux %d+%d+%d+%d * 4 = %d bytes",
pDvmDex, stringCount, classCount, methodCount, fieldCount,
(stringCount + classCount + methodCount + fieldCount) * 4);
pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);
if (pDvmDex->pResStrings == NULL ||
pDvmDex->pResClasses == NULL ||
pDvmDex->pResMethods == NULL ||
pDvmDex->pResFields == NULL ||
pDvmDex->pInterfaceCache == NULL)
{
LOGE("Alloc failure in allocateAuxStructures");
free(pDvmDex->pResStrings);
free(pDvmDex->pResClasses);
free(pDvmDex->pResMethods);
free(pDvmDex->pResFields);
free(pDvmDex);
return NULL;
}
return pDvmDex;
}
/*
* Given an open optimized DEX file, map it into read-only shared memory and
* parse the contents.
*
* Returns nonzero on error.
*/
int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
{
DvmDex* pDvmDex;
DexFile* pDexFile;
MemMapping memMap;
int parseFlags = kDexParseDefault;
int result = -1;
if (gDvm.verifyDexChecksum)
parseFlags |= kDexParseVerifyChecksum;
if (lseek(fd, 0, SEEK_SET) < 0) {
LOGE("lseek rewind failed");
goto bail;
}
if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
LOGE("Unable to map file");
goto bail;
}
pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
if (pDexFile == NULL) {
LOGE("DEX parse failed");
sysReleaseShmem(&memMap);
goto bail;
}
pDvmDex = allocateAuxStructures(pDexFile);
if (pDvmDex == NULL) {
dexFileFree(pDexFile);
sysReleaseShmem(&memMap);
goto bail;
}
/* tuck this into the DexFile so it gets released later */
sysCopyMap(&pDvmDex->memMap, &memMap);
pDvmDex->isMappedReadOnly = true;
*ppDvmDex = pDvmDex;
result = 0;
bail:
return result;
}
/*
* Create a DexFile structure for a "partial" DEX. This is one that is in
* the process of being optimized. The optimization header isn't finished
* and we won't have any of the auxillary data tables, so we have to do
* the initialization slightly differently.
*
* Returns nonzero on error.
*/
int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
{
DvmDex* pDvmDex;
DexFile* pDexFile;
int parseFlags = kDexParseDefault;
int result = -1;
/* -- file is incomplete, new checksum has not yet been calculated
if (gDvm.verifyDexChecksum)
parseFlags |= kDexParseVerifyChecksum;
*/
pDexFile = dexFileParse((u1*)addr, len, parseFlags);
if (pDexFile == NULL) {
LOGE("DEX parse failed");
goto bail;
}
pDvmDex = allocateAuxStructures(pDexFile);
if (pDvmDex == NULL) {
dexFileFree(pDexFile);
goto bail;
}
pDvmDex->isMappedReadOnly = false;
*ppDvmDex = pDvmDex;
result = 0;
bail:
return result;
}
/*
* Free up the DexFile and any associated data structures.
*
* Note we may be called with a partially-initialized DvmDex.
*/
void dvmDexFileFree(DvmDex* pDvmDex)
{
if (pDvmDex == NULL)
return;
dexFileFree(pDvmDex->pDexFile);
LOGV("+++ DEX %p: freeing aux structs", pDvmDex);
free(pDvmDex->pResStrings);
free(pDvmDex->pResClasses);
free(pDvmDex->pResMethods);
free(pDvmDex->pResFields);
dvmFreeAtomicCache(pDvmDex->pInterfaceCache);
sysReleaseShmem(&pDvmDex->memMap);
free(pDvmDex);
}
/*
* Change the byte at the specified address to a new value. If the location
* already has the new value, do nothing.
*
* This requires changing the access permissions to read-write, updating
* the value, and then resetting the permissions.
*
* We need to ensure mutual exclusion at a page granularity to avoid a race
* where one threads sets read-write, another thread sets read-only, and
* then the first thread does a write. Since we don't do a lot of updates,
* and the window is small, we just use a lock across the entire DvmDex.
* We're only trying to make the page state change atomic; it's up to the
* caller to ensure that multiple threads aren't stomping on the same
* location (e.g. breakpoints and verifier/optimizer changes happening
* simultaneously).
*
* TODO: if we're back to the original state of the page, use
* madvise(MADV_DONTNEED) to release the private/dirty copy.
*
* Returns "true" on success.
*/
bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal)
{
if (*addr == newVal) {
LOGV("+++ byte at %p is already 0x%02x", addr, newVal);
return true;
}
/*
* We're not holding this for long, so we don't bother with switching
* to VMWAIT.
*/
dvmLockMutex(&pDvmDex->modLock);
LOGV("+++ change byte at %p from 0x%02x to 0x%02x", addr, *addr, newVal);
if (sysChangeMapAccess(addr, 1, true, &pDvmDex->memMap) != 0) {
LOGD("NOTE: DEX page access change (->RW) failed");
/* expected on files mounted from FAT; keep going (may crash) */
}
*addr = newVal;
if (sysChangeMapAccess(addr, 1, false, &pDvmDex->memMap) != 0) {
LOGD("NOTE: DEX page access change (->RO) failed");
/* expected on files mounted from FAT; keep going */
}
dvmUnlockMutex(&pDvmDex->modLock);
return true;
}
/*
* Change the 2-byte value at the specified address to a new value. If the
* location already has the new value, do nothing.
*
* Otherwise works like dvmDexChangeDex1.
*/
bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal)
{
if (*addr == newVal) {
LOGV("+++ value at %p is already 0x%04x", addr, newVal);
return true;
}
/*
* We're not holding this for long, so we don't bother with switching
* to VMWAIT.
*/
dvmLockMutex(&pDvmDex->modLock);
LOGV("+++ change 2byte at %p from 0x%04x to 0x%04x", addr, *addr, newVal);
if (sysChangeMapAccess(addr, 2, true, &pDvmDex->memMap) != 0) {
LOGD("NOTE: DEX page access change (->RW) failed");
/* expected on files mounted from FAT; keep going (may crash) */
}
*addr = newVal;
if (sysChangeMapAccess(addr, 2, false, &pDvmDex->memMap) != 0) {
LOGD("NOTE: DEX page access change (->RO) failed");
/* expected on files mounted from FAT; keep going */
}
dvmUnlockMutex(&pDvmDex->modLock);
return true;
}