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