OSDN Git Service

am b5d6f9d5: am d6b31b85: am 2366260f: am 3e905b77: Delete libcore, now in its own...
[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 into a shared, read-only memory segment.  The "start"
270  * offset is absolute, not relative.
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, size_t length,
276     MemMapping* pMap)
277 {
278 #ifdef HAVE_POSIX_FILEMAP
279     size_t actualLength;
280     off_t actualStart;
281     int adjust;
282     void* memPtr;
283
284     assert(pMap != NULL);
285
286     /* adjust to be page-aligned */
287     adjust = start % DEFAULT_PAGE_SIZE;
288     actualStart = start - adjust;
289     actualLength = length + adjust;
290
291     memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
292                 fd, actualStart);
293     if (memPtr == MAP_FAILED) {
294         LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
295             (int) actualLength, fd, (int) actualStart, strerror(errno));
296         return -1;
297     }
298
299     pMap->baseAddr = memPtr;
300     pMap->baseLength = actualLength;
301     pMap->addr = (char*)memPtr + adjust;
302     pMap->length = length;
303
304     LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
305         (int) start, (int) length,
306         pMap->baseAddr, (int) pMap->baseLength,
307         pMap->addr, (int) pMap->length);
308
309     return 0;
310 #else
311     LOGE("sysMapFileSegmentInShmem not implemented.\n");
312     return -1;
313 #endif
314 }
315
316 /*
317  * Change the access rights on one or more pages to read-only or read-write.
318  *
319  * Returns 0 on success.
320  */
321 int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
322     MemMapping* pMap)
323 {
324 #ifdef HAVE_POSIX_FILEMAP
325     /*
326      * Verify that "addr" is part of this mapping file.
327      */
328     if (addr < pMap->baseAddr ||
329         (u1*)addr >= (u1*)pMap->baseAddr + pMap->baseLength)
330     {
331         LOGE("Attempted to change %p; map is %p - %p\n",
332             addr, pMap->baseAddr, (u1*)pMap->baseAddr + pMap->baseLength);
333         return -1;
334     }
335
336     /*
337      * Align "addr" to a page boundary and adjust "length" appropriately.
338      * (The address must be page-aligned, the length doesn't need to be,
339      * but we do need to ensure we cover the same range.)
340      */
341     u1* alignAddr = (u1*) ((int) addr & ~(SYSTEM_PAGE_SIZE-1));
342     size_t alignLength = length + ((u1*) addr - alignAddr);
343
344     //LOGI("%p/%zd --> %p/%zd\n", addr, length, alignAddr, alignLength);
345     int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ);
346     if (mprotect(alignAddr, alignLength, prot) != 0) {
347         int err = errno;
348         LOGV("mprotect (%p,%zd,%d) failed: %s\n",
349             alignAddr, alignLength, prot, strerror(errno));
350         return (errno != 0) ? errno : -1;
351     }
352 #endif
353
354     /* for "fake" mapping, no need to do anything */
355     return 0;
356 }
357
358 /*
359  * Release a memory mapping.
360  */
361 void sysReleaseShmem(MemMapping* pMap)
362 {
363 #ifdef HAVE_POSIX_FILEMAP
364     if (pMap->baseAddr == NULL && pMap->baseLength == 0)
365         return;
366
367     if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
368         LOGW("munmap(%p, %d) failed: %s\n",
369             pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
370     } else {
371         LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
372         pMap->baseAddr = NULL;
373         pMap->baseLength = 0;
374     }
375 #else
376     /* Free the bits allocated by sysMapFileInShmem. */
377     if (pMap->baseAddr != NULL) {
378       free(pMap->baseAddr);
379       pMap->baseAddr = NULL;
380     }
381     pMap->baseLength = 0;
382 #endif
383 }
384
385 /*
386  * Make a copy of a MemMapping.
387  */
388 void sysCopyMap(MemMapping* dst, const MemMapping* src)
389 {
390     memcpy(dst, src, sizeof(MemMapping));
391 }
392