DvmDex.cpp revision 92c1f6f1b4249e4e379452ee7b49f027052bf4ce
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 */
40static 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 */
103int 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
144bail:
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 */
156int 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
183bail:
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 */
192void 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 */
232bool 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 */
269bool 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}
299