OSDN Git Service

Separately sweep the monitor list before sweeping the heap. Required
[android-x86/dalvik.git] / libdex / SysUtil.c
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  * System utilities.
19  */
20 #include "DexFile.h"
21 #include "SysUtil.h"
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <string.h>
27
28 #ifdef HAVE_POSIX_FILEMAP
29 #include <sys/mman.h>
30 #endif
31
32 #include <limits.h>
33 #include <errno.h>
34
35 /*
36  * Having trouble finding a portable way to get this.  sysconf(_SC_PAGE_SIZE)
37  * seems appropriate, but we don't have that on the device.  Some systems
38  * have getpagesize(2), though the linux man page has some odd cautions.
39  */
40 #define DEFAULT_PAGE_SIZE   4096
41
42
43 /*
44  * Create an anonymous shared memory segment large enough to hold "length"
45  * bytes.  The actual segment may be larger because mmap() operates on
46  * page boundaries (usually 4K).
47  */
48 static void* sysCreateAnonShmem(size_t length)
49 {
50 #ifdef HAVE_POSIX_FILEMAP
51     void* ptr;
52
53     ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
54             MAP_SHARED | MAP_ANON, -1, 0);
55     if (ptr == MAP_FAILED) {
56         LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
57             strerror(errno));
58         return NULL;
59     }
60
61     return ptr;
62 #else
63     LOGE("sysCreateAnonShmem not implemented.\n");
64     return NULL;
65 #endif
66 }
67
68 /*
69  * Create a private anonymous storage area.
70  */
71 int sysCreatePrivateMap(size_t length, MemMapping* pMap)
72 {
73     void* memPtr;
74
75     memPtr = sysCreateAnonShmem(length);
76     if (memPtr == NULL)
77         return -1;
78
79     pMap->addr = pMap->baseAddr = memPtr;
80     pMap->length = pMap->baseLength = length;
81     return 0;
82 }
83
84 /*
85  * Determine the current offset and remaining length of the open file.
86  */
87 static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
88 {
89     off_t start, end;
90     size_t length;
91
92     assert(start_ != NULL);
93     assert(length_ != NULL);
94
95     start = lseek(fd, 0L, SEEK_CUR);
96     end = lseek(fd, 0L, SEEK_END);
97     (void) lseek(fd, start, SEEK_SET);
98
99     if (start == (off_t) -1 || end == (off_t) -1) {
100         LOGE("could not determine length of file\n");
101         return -1;
102     }
103
104     length = end - start;
105     if (length == 0) {
106         LOGE("file is empty\n");
107         return -1;
108     }
109
110     *start_ = start;
111     *length_ = length;
112
113     return 0;
114 }
115
116 /*
117  * Pull the contents of a file into an new shared memory segment.  We grab
118  * everything from fd's current offset on.
119  *
120  * We need to know the length ahead of time so we can allocate a segment
121  * of sufficient size.
122  */
123 int sysLoadFileInShmem(int fd, MemMapping* pMap)
124 {
125 #ifdef HAVE_POSIX_FILEMAP
126     off_t start;
127     size_t length, actual;
128     void* memPtr;
129
130     assert(pMap != NULL);
131
132     if (getFileStartAndLength(fd, &start, &length) < 0)
133         return -1;
134
135     memPtr = sysCreateAnonShmem(length);
136     if (memPtr == NULL)
137         return -1;
138
139     actual = read(fd, memPtr, length);
140     if (actual != length) {
141         LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
142         sysReleaseShmem(pMap);
143         return -1;
144     }
145
146     pMap->baseAddr = pMap->addr = memPtr;
147     pMap->baseLength = pMap->length = length;
148
149     return 0;
150 #else
151     LOGE("sysLoadFileInShmem not implemented.\n");
152     return -1;
153 #endif
154 }
155
156 #ifndef HAVE_POSIX_FILEMAP
157 int sysFakeMapFile(int fd, MemMapping* pMap)
158 {
159     /* No MMAP, just fake it by copying the bits.
160        For Win32 we could use MapViewOfFile if really necessary
161        (see libs/utils/FileMap.cpp).
162     */
163     off_t start;
164     size_t length;
165     void* memPtr;
166
167     assert(pMap != NULL);
168
169     if (getFileStartAndLength(fd, &start, &length) < 0)
170         return -1;
171
172     memPtr = malloc(length);
173     if (read(fd, memPtr, length) < 0) {
174         LOGW("read(fd=%d, start=%d, length=%d) failed: %s\n", (int) length,
175             fd, (int) start, strerror(errno));
176         return -1;
177     }
178
179     pMap->baseAddr = pMap->addr = memPtr;
180     pMap->baseLength = pMap->length = length;
181
182     return 0;
183 }
184 #endif
185
186 /*
187  * Map a file (from fd's current offset) into a shared, read-only memory
188  * segment.  The file offset must be a multiple of the system page size.
189  *
190  * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
191  * value and does not disturb "pMap".
192  */
193 int sysMapFileInShmemReadOnly(int fd, MemMapping* pMap)
194 {
195 #ifdef HAVE_POSIX_FILEMAP
196     off_t start;
197     size_t length;
198     void* memPtr;
199
200     assert(pMap != NULL);
201
202     if (getFileStartAndLength(fd, &start, &length) < 0)
203         return -1;
204
205     memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
206     if (memPtr == MAP_FAILED) {
207         LOGW("mmap(%d, RO, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
208             fd, (int) start, strerror(errno));
209         return -1;
210     }
211
212     pMap->baseAddr = pMap->addr = memPtr;
213     pMap->baseLength = pMap->length = length;
214
215     return 0;
216 #else
217     return sysFakeMapFile(fd, pMap);
218 #endif
219 }
220
221 /*
222  * Map a file (from fd's current offset) into a private, read-write memory
223  * segment that will be marked read-only (a/k/a "writable read-only").  The
224  * file offset must be a multiple of the system page size.
225  *
226  * In some cases the mapping will be fully writable (e.g. for files on
227  * FAT filesystems).
228  *
229  * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
230  * value and does not disturb "pMap".
231  */
232 int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
233 {
234 #ifdef HAVE_POSIX_FILEMAP
235     off_t start;
236     size_t length;
237     void* memPtr;
238
239     assert(pMap != NULL);
240
241     if (getFileStartAndLength(fd, &start, &length) < 0)
242         return -1;
243
244     memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE,
245             fd, start);
246     if (memPtr == MAP_FAILED) {
247         LOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s\n", (int) length,
248             fd, (int) start, strerror(errno));
249         return -1;
250     }
251     if (mprotect(memPtr, length, PROT_READ) < 0) {
252         /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */
253         int err = errno;
254         LOGV("mprotect(%p, %d, PROT_READ) failed: %s\n",
255             memPtr, length, strerror(err));
256         LOGD("mprotect(RO) failed (%d), file will remain read-write\n", err);
257     }
258
259     pMap->baseAddr = pMap->addr = memPtr;
260     pMap->baseLength = pMap->length = length;
261
262     return 0;
263 #else
264     return sysFakeMapFile(fd, pMap);
265 #endif
266 }
267
268 /*
269  * Map part of a file (from fd's current offset) into a shared, read-only
270  * memory segment.
271  *
272  * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
273  * value and does not disturb "pMap".
274  */
275 int sysMapFileSegmentInShmem(int fd, off_t start, long length,
276     MemMapping* pMap)
277 {
278 #ifdef HAVE_POSIX_FILEMAP
279     off_t dummy;
280     size_t fileLength, actualLength;
281     off_t actualStart;
282     int adjust;
283     void* memPtr;
284
285     assert(pMap != NULL);
286
287     if (getFileStartAndLength(fd, &dummy, &fileLength) < 0)
288         return -1;
289
290     if (start + length > (long)fileLength) {
291         LOGW("bad segment: st=%d len=%ld flen=%d\n",
292             (int) start, length, (int) fileLength);
293         return -1;
294     }
295
296     /* adjust to be page-aligned */
297     adjust = start % DEFAULT_PAGE_SIZE;
298     actualStart = start - adjust;
299     actualLength = length + adjust;
300
301     memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
302                 fd, actualStart);
303     if (memPtr == MAP_FAILED) {
304         LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
305             (int) actualLength, fd, (int) actualStart, strerror(errno));
306         return -1;
307     }
308
309     pMap->baseAddr = memPtr;
310     pMap->baseLength = actualLength;
311     pMap->addr = (char*)memPtr + adjust;
312     pMap->length = length;
313
314     LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
315         (int) start, (int) length,
316         pMap->baseAddr, (int) pMap->baseLength,
317         pMap->addr, (int) pMap->length);
318
319     return 0;
320 #else
321     LOGE("sysMapFileSegmentInShmem not implemented.\n");
322     return -1;
323 #endif
324 }
325
326 /*
327  * Change the access rights on one or more pages to read-only or read-write.
328  *
329  * Returns 0 on success.
330  */
331 int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
332     MemMapping* pMap)
333 {
334 #ifdef HAVE_POSIX_FILEMAP
335     /*
336      * Verify that "addr" is part of this mapping file.
337      */
338     if (addr < pMap->baseAddr ||
339         (u1*)addr >= (u1*)pMap->baseAddr + pMap->baseLength)
340     {
341         LOGE("Attempted to change %p; map is %p - %p\n",
342             addr, pMap->baseAddr, (u1*)pMap->baseAddr + pMap->baseLength);
343         return -1;
344     }
345
346     /*
347      * Align "addr" to a page boundary and adjust "length" appropriately.
348      * (The address must be page-aligned, the length doesn't need to be,
349      * but we do need to ensure we cover the same range.)
350      */
351     u1* alignAddr = (u1*) ((int) addr & ~(SYSTEM_PAGE_SIZE-1));
352     size_t alignLength = length + ((u1*) addr - alignAddr);
353
354     //LOGI("%p/%zd --> %p/%zd\n", addr, length, alignAddr, alignLength);
355     int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ);
356     if (mprotect(alignAddr, alignLength, prot) != 0) {
357         int err = errno;
358         LOGV("mprotect (%p,%zd,%d) failed: %s\n",
359             alignAddr, alignLength, prot, strerror(errno));
360         return (errno != 0) ? errno : -1;
361     }
362 #endif
363
364     /* for "fake" mapping, no need to do anything */
365     return 0;
366 }
367
368 /*
369  * Release a memory mapping.
370  */
371 void sysReleaseShmem(MemMapping* pMap)
372 {
373 #ifdef HAVE_POSIX_FILEMAP
374     if (pMap->baseAddr == NULL && pMap->baseLength == 0)
375         return;
376
377     if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
378         LOGW("munmap(%p, %d) failed: %s\n",
379             pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
380     } else {
381         LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
382         pMap->baseAddr = NULL;
383         pMap->baseLength = 0;
384     }
385 #else
386     /* Free the bits allocated by sysMapFileInShmem. */
387     if (pMap->baseAddr != NULL) {
388       free(pMap->baseAddr);
389       pMap->baseAddr = NULL;
390     }
391     pMap->baseLength = 0;
392 #endif
393 }
394
395 /*
396  * Make a copy of a MemMapping.
397  */
398 void sysCopyMap(MemMapping* dst, const MemMapping* src)
399 {
400     memcpy(dst, src, sizeof(MemMapping));
401 }
402