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
allocateAuxStructures(DexFile * pDexFile)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 */
dvmDexFileOpenFromFd(int fd,DvmDex ** ppDvmDex)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 */
dvmDexFileOpenPartial(const void * addr,int len,DvmDex ** ppDvmDex)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 */
dvmDexFileFree(DvmDex * pDvmDex)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 */
dvmDexChangeDex1(DvmDex * pDvmDex,u1 * addr,u1 newVal)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 */
dvmDexChangeDex2(DvmDex * pDvmDex,u2 * addr,u2 newVal)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 }
290