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