OSDN Git Service

am 707ebe9f: Fix alignment when recompacting a DexMerger result. do not merge.
[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 #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     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 }