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
41static 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 */
93int 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
134bail:
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 */
146int 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
173bail:
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 */
182void 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 */
227bool 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 */
264bool 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