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