Home | History | Annotate | Download | only in vm
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * VM-specific state associated with a DEX file.
     19  */
     20 #include "Dalvik.h"
     21 #include <sys/mman.h>
     22 
     23 /*
     24  * Create auxillary data structures.
     25  *
     26  * We need a 4-byte pointer for every reference to a class, method, field,
     27  * or string constant.  Summed up over all loaded DEX files (including the
     28  * whoppers in the boostrap class path), this adds up to be quite a bit
     29  * of native memory.
     30  *
     31  * For more traditional VMs these values could be stuffed into the loaded
     32  * class file constant pool area, but we don't have that luxury since our
     33  * classes are memory-mapped read-only.
     34  *
     35  * The DEX optimizer will remove the need for some of these (e.g. we won't
     36  * use the entry for virtual methods that are only called through
     37  * invoke-virtual-quick), creating the possibility of some space reduction
     38  * at dexopt time.
     39  */
     40 
     41 static DvmDex* allocateAuxStructures(DexFile* pDexFile)
     42 {
     43     DvmDex* pDvmDex;
     44     const DexHeader* pHeader;
     45     u4 stringSize, classSize, methodSize, fieldSize;
     46 
     47     pHeader = pDexFile->pHeader;
     48 
     49     stringSize = pHeader->stringIdsSize * sizeof(struct StringObject*);
     50     classSize  = pHeader->typeIdsSize * sizeof(struct ClassObject*);
     51     methodSize = pHeader->methodIdsSize * sizeof(struct Method*);
     52     fieldSize  = pHeader->fieldIdsSize * sizeof(struct Field*);
     53 
     54     u4 totalSize = sizeof(DvmDex) +
     55                    stringSize + classSize + methodSize + fieldSize;
     56 
     57     u1 *blob = (u1 *)dvmAllocRegion(totalSize,
     58                               PROT_READ | PROT_WRITE, "dalvik-aux-structure");
     59     if ((void *)blob == MAP_FAILED)
     60         return NULL;
     61 
     62     pDvmDex = (DvmDex*)blob;
     63     blob += sizeof(DvmDex);
     64 
     65     pDvmDex->pDexFile = pDexFile;
     66     pDvmDex->pHeader = pHeader;
     67 
     68     pDvmDex->pResStrings = (struct StringObject**)blob;
     69     blob += stringSize;
     70     pDvmDex->pResClasses = (struct ClassObject**)blob;
     71     blob += classSize;
     72     pDvmDex->pResMethods = (struct Method**)blob;
     73     blob += methodSize;
     74     pDvmDex->pResFields = (struct Field**)blob;
     75 
     76     ALOGV("+++ DEX %p: allocateAux (%d+%d+%d+%d)*4 = %d bytes",
     77         pDvmDex, stringSize/4, classSize/4, methodSize/4, fieldSize/4,
     78         stringSize + classSize + methodSize + fieldSize);
     79 
     80     pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);
     81 
     82     dvmInitMutex(&pDvmDex->modLock);
     83 
     84     return pDvmDex;
     85 }
     86 
     87 /*
     88  * Given an open optimized DEX file, map it into read-only shared memory and
     89  * parse the contents.
     90  *
     91  * Returns nonzero on error.
     92  */
     93 int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
     94 {
     95     DvmDex* pDvmDex;
     96     DexFile* pDexFile;
     97     MemMapping memMap;
     98     int parseFlags = kDexParseDefault;
     99     int result = -1;
    100 
    101     if (gDvm.verifyDexChecksum)
    102         parseFlags |= kDexParseVerifyChecksum;
    103 
    104     if (lseek(fd, 0, SEEK_SET) < 0) {
    105         ALOGE("lseek rewind failed");
    106         goto bail;
    107     }
    108 
    109     if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
    110         ALOGE("Unable to map file");
    111         goto bail;
    112     }
    113 
    114     pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
    115     if (pDexFile == NULL) {
    116         ALOGE("DEX parse failed");
    117         sysReleaseShmem(&memMap);
    118         goto bail;
    119     }
    120 
    121     pDvmDex = allocateAuxStructures(pDexFile);
    122     if (pDvmDex == NULL) {
    123         dexFileFree(pDexFile);
    124         sysReleaseShmem(&memMap);
    125         goto bail;
    126     }
    127 
    128     /* tuck this into the DexFile so it gets released later */
    129     sysCopyMap(&pDvmDex->memMap, &memMap);
    130     pDvmDex->isMappedReadOnly = true;
    131     *ppDvmDex = pDvmDex;
    132     result = 0;
    133 
    134 bail:
    135     return result;
    136 }
    137 
    138 /*
    139  * Create a DexFile structure for a "partial" DEX.  This is one that is in
    140  * the process of being optimized.  The optimization header isn't finished
    141  * and we won't have any of the auxillary data tables, so we have to do
    142  * the initialization slightly differently.
    143  *
    144  * Returns nonzero on error.
    145  */
    146 int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
    147 {
    148     DvmDex* pDvmDex;
    149     DexFile* pDexFile;
    150     int parseFlags = kDexParseDefault;
    151     int result = -1;
    152 
    153     /* -- file is incomplete, new checksum has not yet been calculated
    154     if (gDvm.verifyDexChecksum)
    155         parseFlags |= kDexParseVerifyChecksum;
    156     */
    157 
    158     pDexFile = dexFileParse((u1*)addr, len, parseFlags);
    159     if (pDexFile == NULL) {
    160         ALOGE("DEX parse failed");
    161         goto bail;
    162     }
    163     pDvmDex = allocateAuxStructures(pDexFile);
    164     if (pDvmDex == NULL) {
    165         dexFileFree(pDexFile);
    166         goto bail;
    167     }
    168 
    169     pDvmDex->isMappedReadOnly = false;
    170     *ppDvmDex = pDvmDex;
    171     result = 0;
    172 
    173 bail:
    174     return result;
    175 }
    176 
    177 /*
    178  * Free up the DexFile and any associated data structures.
    179  *
    180  * Note we may be called with a partially-initialized DvmDex.
    181  */
    182 void dvmDexFileFree(DvmDex* pDvmDex)
    183 {
    184     u4 totalSize;
    185 
    186     if (pDvmDex == NULL)
    187         return;
    188 
    189     dvmDestroyMutex(&pDvmDex->modLock);
    190 
    191     totalSize  = pDvmDex->pHeader->stringIdsSize * sizeof(struct StringObject*);
    192     totalSize += pDvmDex->pHeader->typeIdsSize * sizeof(struct ClassObject*);
    193     totalSize += pDvmDex->pHeader->methodIdsSize * sizeof(struct Method*);
    194     totalSize += pDvmDex->pHeader->fieldIdsSize * sizeof(struct Field*);
    195     totalSize += sizeof(DvmDex);
    196 
    197     dexFileFree(pDvmDex->pDexFile);
    198 
    199     ALOGV("+++ DEX %p: freeing aux structs", pDvmDex);
    200     dvmFreeAtomicCache(pDvmDex->pInterfaceCache);
    201     sysReleaseShmem(&pDvmDex->memMap);
    202     munmap(pDvmDex, totalSize);
    203 }
    204 
    205 
    206 /*
    207  * Change the byte at the specified address to a new value.  If the location
    208  * already has the new value, do nothing.
    209  *
    210  * This requires changing the access permissions to read-write, updating
    211  * the value, and then resetting the permissions.
    212  *
    213  * We need to ensure mutual exclusion at a page granularity to avoid a race
    214  * where one threads sets read-write, another thread sets read-only, and
    215  * then the first thread does a write.  Since we don't do a lot of updates,
    216  * and the window is small, we just use a lock across the entire DvmDex.
    217  * We're only trying to make the page state change atomic; it's up to the
    218  * caller to ensure that multiple threads aren't stomping on the same
    219  * location (e.g. breakpoints and verifier/optimizer changes happening
    220  * simultaneously).
    221  *
    222  * TODO: if we're back to the original state of the page, use
    223  * madvise(MADV_DONTNEED) to release the private/dirty copy.
    224  *
    225  * Returns "true" on success.
    226  */
    227 bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal)
    228 {
    229     if (*addr == newVal) {
    230         ALOGV("+++ byte at %p is already 0x%02x", addr, newVal);
    231         return true;
    232     }
    233 
    234     /*
    235      * We're not holding this for long, so we don't bother with switching
    236      * to VMWAIT.
    237      */
    238     dvmLockMutex(&pDvmDex->modLock);
    239 
    240     ALOGV("+++ change byte at %p from 0x%02x to 0x%02x", addr, *addr, newVal);
    241     if (sysChangeMapAccess(addr, 1, true, &pDvmDex->memMap) != 0) {
    242         ALOGD("NOTE: DEX page access change (->RW) failed");
    243         /* expected on files mounted from FAT; keep going (may crash) */
    244     }
    245 
    246     *addr = newVal;
    247 
    248     if (sysChangeMapAccess(addr, 1, false, &pDvmDex->memMap) != 0) {
    249         ALOGD("NOTE: DEX page access change (->RO) failed");
    250         /* expected on files mounted from FAT; keep going */
    251     }
    252 
    253     dvmUnlockMutex(&pDvmDex->modLock);
    254 
    255     return true;
    256 }
    257 
    258 /*
    259  * Change the 2-byte value at the specified address to a new value.  If the
    260  * location already has the new value, do nothing.
    261  *
    262  * Otherwise works like dvmDexChangeDex1.
    263  */
    264 bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal)
    265 {
    266     if (*addr == newVal) {
    267         ALOGV("+++ value at %p is already 0x%04x", addr, newVal);
    268         return true;
    269     }
    270 
    271     /*
    272      * We're not holding this for long, so we don't bother with switching
    273      * to VMWAIT.
    274      */
    275     dvmLockMutex(&pDvmDex->modLock);
    276 
    277     ALOGV("+++ change 2byte at %p from 0x%04x to 0x%04x", addr, *addr, newVal);
    278     if (sysChangeMapAccess(addr, 2, true, &pDvmDex->memMap) != 0) {
    279         ALOGD("NOTE: DEX page access change (->RW) failed");
    280         /* expected on files mounted from FAT; keep going (may crash) */
    281     }
    282 
    283     *addr = newVal;
    284 
    285     if (sysChangeMapAccess(addr, 2, false, &pDvmDex->memMap) != 0) {
    286         ALOGD("NOTE: DEX page access change (->RO) failed");
    287         /* expected on files mounted from FAT; keep going */
    288     }
    289 
    290     dvmUnlockMutex(&pDvmDex->modLock);
    291 
    292     return true;
    293 }
    294