• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      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   */
dvmDexFileOpenFromFd(int fd,DvmDex ** ppDvmDex)93  int 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  
134  bail:
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   */
dvmDexFileOpenPartial(const void * addr,int len,DvmDex ** ppDvmDex)146  int 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  
173  bail:
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   */
dvmDexFileFree(DvmDex * pDvmDex)182  void 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   */
dvmDexChangeDex1(DvmDex * pDvmDex,u1 * addr,u1 newVal)227  bool 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   */
dvmDexChangeDex2(DvmDex * pDvmDex,u2 * addr,u2 newVal)264  bool 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