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