• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
allocateAuxStructures(DexFile * pDexFile)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  */
dvmDexFileOpenFromFd(int fd,DvmDex ** ppDvmDex)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  */
dvmDexFileOpenPartial(const void * addr,int len,DvmDex ** ppDvmDex)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  */
dvmDexFileFree(DvmDex * pDvmDex)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  */
dvmDexChangeDex1(DvmDex * pDvmDex,u1 * addr,u1 newVal)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  */
dvmDexChangeDex2(DvmDex * pDvmDex,u2 * addr,u2 newVal)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