OSDN Git Service

Reconcile with ics-mr1-release
[android-x86/dalvik.git] / vm / DvmDex.cpp
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     ALOGV("+++ 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     ALOGV("+++ 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         ALOGV("+++ 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     ALOGV("+++ 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         ALOGV("+++ 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     ALOGV("+++ 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 }