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 */
allocateAuxStructures(DexFile * pDexFile)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 LOGV("+++ 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 */
dvmDexFileOpenFromFd(int fd,DvmDex ** ppDvmDex)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 */
dvmDexFileOpenPartial(const void * addr,int len,DvmDex ** ppDvmDex)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 */
dvmDexFileFree(DvmDex * pDvmDex)192 void dvmDexFileFree(DvmDex* pDvmDex)
193 {
194 if (pDvmDex == NULL)
195 return;
196
197 dexFileFree(pDvmDex->pDexFile);
198
199 LOGV("+++ 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 */
dvmDexChangeDex1(DvmDex * pDvmDex,u1 * addr,u1 newVal)232 bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal)
233 {
234 if (*addr == newVal) {
235 LOGV("+++ 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 LOGV("+++ 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 */
dvmDexChangeDex2(DvmDex * pDvmDex,u2 * addr,u2 newVal)269 bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal)
270 {
271 if (*addr == newVal) {
272 LOGV("+++ 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 LOGV("+++ 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