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 
     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 static DvmDex* allocateAuxStructures(DexFile* pDexFile)
     41 {
     42     DvmDex* pDvmDex;
     43     const DexHeader* pHeader;
     44     u4 stringCount, classCount, methodCount, fieldCount;
     45 
     46     pDvmDex = (DvmDex*) calloc(1, sizeof(DvmDex));
     47     if (pDvmDex == NULL)
     48         return NULL;
     49 
     50     pDvmDex->pDexFile = pDexFile;
     51     pDvmDex->pHeader = pDexFile->pHeader;
     52 
     53     pHeader = pDvmDex->pHeader;
     54 
     55     stringCount = pHeader->stringIdsSize;
     56     classCount = pHeader->typeIdsSize;
     57     methodCount = pHeader->methodIdsSize;
     58     fieldCount = pHeader->fieldIdsSize;
     59 
     60     pDvmDex->pResStrings = (struct StringObject**)
     61         calloc(stringCount, sizeof(struct StringObject*));
     62 
     63     pDvmDex->pResClasses = (struct ClassObject**)
     64         calloc(classCount, sizeof(struct ClassObject*));
     65 
     66     pDvmDex->pResMethods = (struct Method**)
     67         calloc(methodCount, sizeof(struct Method*));
     68 
     69     pDvmDex->pResFields = (struct Field**)
     70         calloc(fieldCount, sizeof(struct Field*));
     71 
     72     LOGV("+++ DEX %p: allocateAux %d+%d+%d+%d * 4 = %d bytes",
     73         pDvmDex, stringCount, classCount, methodCount, fieldCount,
     74         (stringCount + classCount + methodCount + fieldCount) * 4);
     75 
     76     pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);
     77 
     78     if (pDvmDex->pResStrings == NULL ||
     79         pDvmDex->pResClasses == NULL ||
     80         pDvmDex->pResMethods == NULL ||
     81         pDvmDex->pResFields == NULL ||
     82         pDvmDex->pInterfaceCache == NULL)
     83     {
     84         LOGE("Alloc failure in allocateAuxStructures");
     85         free(pDvmDex->pResStrings);
     86         free(pDvmDex->pResClasses);
     87         free(pDvmDex->pResMethods);
     88         free(pDvmDex->pResFields);
     89         free(pDvmDex);
     90         return NULL;
     91     }
     92 
     93     return pDvmDex;
     94 
     95 }
     96 
     97 /*
     98  * Given an open optimized DEX file, map it into read-only shared memory and
     99  * parse the contents.
    100  *
    101  * Returns nonzero on error.
    102  */
    103 int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
    104 {
    105     DvmDex* pDvmDex;
    106     DexFile* pDexFile;
    107     MemMapping memMap;
    108     int parseFlags = kDexParseDefault;
    109     int result = -1;
    110 
    111     if (gDvm.verifyDexChecksum)
    112         parseFlags |= kDexParseVerifyChecksum;
    113 
    114     if (lseek(fd, 0, SEEK_SET) < 0) {
    115         LOGE("lseek rewind failed");
    116         goto bail;
    117     }
    118 
    119     if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
    120         LOGE("Unable to map file");
    121         goto bail;
    122     }
    123 
    124     pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
    125     if (pDexFile == NULL) {
    126         LOGE("DEX parse failed");
    127         sysReleaseShmem(&memMap);
    128         goto bail;
    129     }
    130 
    131     pDvmDex = allocateAuxStructures(pDexFile);
    132     if (pDvmDex == NULL) {
    133         dexFileFree(pDexFile);
    134         sysReleaseShmem(&memMap);
    135         goto bail;
    136     }
    137 
    138     /* tuck this into the DexFile so it gets released later */
    139     sysCopyMap(&pDvmDex->memMap, &memMap);
    140     pDvmDex->isMappedReadOnly = true;
    141     *ppDvmDex = pDvmDex;
    142     result = 0;
    143 
    144 bail:
    145     return result;
    146 }
    147 
    148 /*
    149  * Create a DexFile structure for a "partial" DEX.  This is one that is in
    150  * the process of being optimized.  The optimization header isn't finished
    151  * and we won't have any of the auxillary data tables, so we have to do
    152  * the initialization slightly differently.
    153  *
    154  * Returns nonzero on error.
    155  */
    156 int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
    157 {
    158     DvmDex* pDvmDex;
    159     DexFile* pDexFile;
    160     int parseFlags = kDexParseDefault;
    161     int result = -1;
    162 
    163     /* -- file is incomplete, new checksum has not yet been calculated
    164     if (gDvm.verifyDexChecksum)
    165         parseFlags |= kDexParseVerifyChecksum;
    166     */
    167 
    168     pDexFile = dexFileParse((u1*)addr, len, parseFlags);
    169     if (pDexFile == NULL) {
    170         LOGE("DEX parse failed");
    171         goto bail;
    172     }
    173     pDvmDex = allocateAuxStructures(pDexFile);
    174     if (pDvmDex == NULL) {
    175         dexFileFree(pDexFile);
    176         goto bail;
    177     }
    178 
    179     pDvmDex->isMappedReadOnly = false;
    180     *ppDvmDex = pDvmDex;
    181     result = 0;
    182 
    183 bail:
    184     return result;
    185 }
    186 
    187 /*
    188  * Free up the DexFile and any associated data structures.
    189  *
    190  * Note we may be called with a partially-initialized DvmDex.
    191  */
    192 void dvmDexFileFree(DvmDex* pDvmDex)
    193 {
    194     if (pDvmDex == NULL)
    195         return;
    196 
    197     dexFileFree(pDvmDex->pDexFile);
    198 
    199     LOGV("+++ DEX %p: freeing aux structs", pDvmDex);
    200     free(pDvmDex->pResStrings);
    201     free(pDvmDex->pResClasses);
    202     free(pDvmDex->pResMethods);
    203     free(pDvmDex->pResFields);
    204     dvmFreeAtomicCache(pDvmDex->pInterfaceCache);
    205 
    206     sysReleaseShmem(&pDvmDex->memMap);
    207     free(pDvmDex);
    208 }
    209 
    210 
    211 /*
    212  * Change the byte at the specified address to a new value.  If the location
    213  * already has the new value, do nothing.
    214  *
    215  * This requires changing the access permissions to read-write, updating
    216  * the value, and then resetting the permissions.
    217  *
    218  * We need to ensure mutual exclusion at a page granularity to avoid a race
    219  * where one threads sets read-write, another thread sets read-only, and
    220  * then the first thread does a write.  Since we don't do a lot of updates,
    221  * and the window is small, we just use a lock across the entire DvmDex.
    222  * We're only trying to make the page state change atomic; it's up to the
    223  * caller to ensure that multiple threads aren't stomping on the same
    224  * location (e.g. breakpoints and verifier/optimizer changes happening
    225  * simultaneously).
    226  *
    227  * TODO: if we're back to the original state of the page, use
    228  * madvise(MADV_DONTNEED) to release the private/dirty copy.
    229  *
    230  * Returns "true" on success.
    231  */
    232 bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal)
    233 {
    234     if (*addr == newVal) {
    235         LOGV("+++ byte at %p is already 0x%02x", addr, newVal);
    236         return true;
    237     }
    238 
    239     /*
    240      * We're not holding this for long, so we don't bother with switching
    241      * to VMWAIT.
    242      */
    243     dvmLockMutex(&pDvmDex->modLock);
    244 
    245     LOGV("+++ change byte at %p from 0x%02x to 0x%02x", addr, *addr, newVal);
    246     if (sysChangeMapAccess(addr, 1, true, &pDvmDex->memMap) != 0) {
    247         LOGD("NOTE: DEX page access change (->RW) failed");
    248         /* expected on files mounted from FAT; keep going (may crash) */
    249     }
    250 
    251     *addr = newVal;
    252 
    253     if (sysChangeMapAccess(addr, 1, false, &pDvmDex->memMap) != 0) {
    254         LOGD("NOTE: DEX page access change (->RO) failed");
    255         /* expected on files mounted from FAT; keep going */
    256     }
    257 
    258     dvmUnlockMutex(&pDvmDex->modLock);
    259 
    260     return true;
    261 }
    262 
    263 /*
    264  * Change the 2-byte value at the specified address to a new value.  If the
    265  * location already has the new value, do nothing.
    266  *
    267  * Otherwise works like dvmDexChangeDex1.
    268  */
    269 bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal)
    270 {
    271     if (*addr == newVal) {
    272         LOGV("+++ value at %p is already 0x%04x", addr, newVal);
    273         return true;
    274     }
    275 
    276     /*
    277      * We're not holding this for long, so we don't bother with switching
    278      * to VMWAIT.
    279      */
    280     dvmLockMutex(&pDvmDex->modLock);
    281 
    282     LOGV("+++ change 2byte at %p from 0x%04x to 0x%04x", addr, *addr, newVal);
    283     if (sysChangeMapAccess(addr, 2, true, &pDvmDex->memMap) != 0) {
    284         LOGD("NOTE: DEX page access change (->RW) failed");
    285         /* expected on files mounted from FAT; keep going (may crash) */
    286     }
    287 
    288     *addr = newVal;
    289 
    290     if (sysChangeMapAccess(addr, 2, false, &pDvmDex->memMap) != 0) {
    291         LOGD("NOTE: DEX page access change (->RO) failed");
    292         /* expected on files mounted from FAT; keep going */
    293     }
    294 
    295     dvmUnlockMutex(&pDvmDex->modLock);
    296 
    297     return true;
    298 }
    299