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