OSDN Git Service

am 548d70b0: am 23966773: Rename absoluteMaxSize to maximumSize.
[android-x86/dalvik.git] / vm / DvmDex.c
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\n",
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\n");
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\n");
116         goto bail;
117     }
118
119     if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
120         LOGE("Unable to map file\n");
121         goto bail;
122     }
123
124     pDexFile = dexFileParse(memMap.addr, memMap.length, parseFlags);
125     if (pDexFile == NULL) {
126         LOGE("DEX parse failed\n");
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     *ppDvmDex = pDvmDex;
141     result = 0;
142
143 bail:
144     return result;
145 }
146
147 /*
148  * Create a DexFile structure for a "partial" DEX.  This is one that is in
149  * the process of being optimized.  The optimization header isn't finished
150  * and we won't have any of the auxillary data tables, so we have to do
151  * the initialization slightly differently.
152  *
153  * Returns nonzero on error.
154  */
155 int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
156 {
157     DvmDex* pDvmDex;
158     DexFile* pDexFile;
159     int parseFlags = kDexParseDefault;
160     int result = -1;
161
162     /* -- file is incomplete, new checksum has not yet been calculated
163     if (gDvm.verifyDexChecksum)
164         parseFlags |= kDexParseVerifyChecksum;
165     */
166
167     pDexFile = dexFileParse(addr, len, parseFlags);
168     if (pDexFile == NULL) {
169         LOGE("DEX parse failed\n");
170         goto bail;
171     }
172     pDvmDex = allocateAuxStructures(pDexFile);
173     if (pDvmDex == NULL) {
174         dexFileFree(pDexFile);
175         goto bail;
176     }
177
178     *ppDvmDex = pDvmDex;
179     result = 0;
180
181 bail:
182     return result;
183 }
184
185 /*
186  * Free up the DexFile and any associated data structures.
187  *
188  * Note we may be called with a partially-initialized DvmDex.
189  */
190 void dvmDexFileFree(DvmDex* pDvmDex)
191 {
192     if (pDvmDex == NULL)
193         return;
194
195     dexFileFree(pDvmDex->pDexFile);
196
197     LOGV("+++ DEX %p: freeing aux structs\n", pDvmDex);
198     free(pDvmDex->pResStrings);
199     free(pDvmDex->pResClasses);
200     free(pDvmDex->pResMethods);
201     free(pDvmDex->pResFields);
202     dvmFreeAtomicCache(pDvmDex->pInterfaceCache);
203
204     sysReleaseShmem(&pDvmDex->memMap);
205     free(pDvmDex);
206 }
207
208
209 /*
210  * Change the byte at the specified address to a new value.  If the location
211  * already has the new value, do nothing.
212  *
213  * This requires changing the access permissions to read-write, updating
214  * the value, and then resetting the permissions.
215  *
216  * We need to ensure mutual exclusion at a page granularity to avoid a race
217  * where one threads sets read-write, another thread sets read-only, and
218  * then the first thread does a write.  Since we don't do a lot of updates,
219  * and the window is small, we just use a lock across the entire DvmDex.
220  * We're only trying to make the page state change atomic; it's up to the
221  * caller to ensure that multiple threads aren't stomping on the same
222  * location (e.g. breakpoints and verifier/optimizer changes happening
223  * simultaneously).
224  *
225  * TODO: if we're back to the original state of the page, use
226  * madvise(MADV_DONTNEED) to release the private/dirty copy.
227  *
228  * Returns "true" on success.
229  */
230 bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal)
231 {
232     if (*addr == newVal) {
233         LOGV("+++ byte at %p is already 0x%02x\n", addr, newVal);
234         return true;
235     }
236
237     /*
238      * We're not holding this for long, so we don't bother with switching
239      * to VMWAIT.
240      */
241     dvmLockMutex(&pDvmDex->modLock);
242
243     LOGV("+++ change byte at %p from 0x%02x to 0x%02x\n", addr, *addr, newVal);
244     if (sysChangeMapAccess(addr, 1, true, &pDvmDex->memMap) != 0) {
245         LOGD("NOTE: DEX page access change (->RW) failed\n");
246         /* expected on files mounted from FAT; keep going (may crash) */
247     }
248
249     *addr = newVal;
250
251     if (sysChangeMapAccess(addr, 1, false, &pDvmDex->memMap) != 0) {
252         LOGD("NOTE: DEX page access change (->RO) failed\n");
253         /* expected on files mounted from FAT; keep going */
254     }
255
256     dvmUnlockMutex(&pDvmDex->modLock);
257
258     return true;
259 }
260
261 /*
262  * Change the 2-byte value at the specified address to a new value.  If the
263  * location already has the new value, do nothing.
264  *
265  * Otherwise works like dvmDexChangeDex1.
266  */
267 bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal)
268 {
269     if (*addr == newVal) {
270         LOGV("+++ value at %p is already 0x%04x\n", addr, newVal);
271         return true;
272     }
273
274     /*
275      * We're not holding this for long, so we don't bother with switching
276      * to VMWAIT.
277      */
278     dvmLockMutex(&pDvmDex->modLock);
279
280     LOGV("+++ change 2byte at %p from 0x%04x to 0x%04x\n", addr, *addr, newVal);
281     if (sysChangeMapAccess(addr, 2, true, &pDvmDex->memMap) != 0) {
282         LOGD("NOTE: DEX page access change (->RW) failed\n");
283         /* expected on files mounted from FAT; keep going (may crash) */
284     }
285
286     *addr = newVal;
287
288     if (sysChangeMapAccess(addr, 2, false, &pDvmDex->memMap) != 0) {
289         LOGD("NOTE: DEX page access change (->RO) failed\n");
290         /* expected on files mounted from FAT; keep going */
291     }
292
293     dvmUnlockMutex(&pDvmDex->modLock);
294
295     return true;
296 }