• 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  * 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  */
sysCreateAnonShmem(size_t length)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  */
sysCreatePrivateMap(size_t length,MemMapping * pMap)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  */
getFileStartAndLength(int fd,off_t * start_,size_t * length_)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  */
sysLoadFileInShmem(int fd,MemMapping * pMap)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 /*
157  * Map a file (from fd's current offset) into a shared, read-only memory
158  * segment.  The file offset must be a multiple of the page size.
159  *
160  * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
161  * value and does not disturb "pMap".
162  */
sysMapFileInShmem(int fd,MemMapping * pMap)163 int sysMapFileInShmem(int fd, MemMapping* pMap)
164 {
165 #ifdef HAVE_POSIX_FILEMAP
166     off_t start;
167     size_t length;
168     void* memPtr;
169 
170     assert(pMap != NULL);
171 
172     if (getFileStartAndLength(fd, &start, &length) < 0)
173         return -1;
174 
175     memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
176     if (memPtr == MAP_FAILED) {
177         LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
178             fd, (int) start, strerror(errno));
179         return -1;
180     }
181 
182     pMap->baseAddr = pMap->addr = memPtr;
183     pMap->baseLength = pMap->length = length;
184 
185     return 0;
186 #else
187     /* No MMAP, just fake it by copying the bits.
188        For Win32 we could use MapViewOfFile if really necessary
189        (see libs/utils/FileMap.cpp).
190     */
191     off_t start;
192     size_t length;
193     void* memPtr;
194 
195     assert(pMap != NULL);
196 
197     if (getFileStartAndLength(fd, &start, &length) < 0)
198         return -1;
199 
200     memPtr = malloc(length);
201     if (read(fd, memPtr, length) < 0) {
202         LOGW("read(fd=%d, start=%d, length=%d) failed: %s\n", (int) length,
203             fd, (int) start, strerror(errno));
204         return -1;
205     }
206 
207     pMap->baseAddr = pMap->addr = memPtr;
208     pMap->baseLength = pMap->length = length;
209 
210     return 0;
211 #endif
212 }
213 
214 /*
215  * Map part of a file (from fd's current offset) into a shared, read-only
216  * memory segment.
217  *
218  * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
219  * value and does not disturb "pMap".
220  */
sysMapFileSegmentInShmem(int fd,off_t start,long length,MemMapping * pMap)221 int sysMapFileSegmentInShmem(int fd, off_t start, long length,
222     MemMapping* pMap)
223 {
224 #ifdef HAVE_POSIX_FILEMAP
225     off_t dummy;
226     size_t fileLength, actualLength;
227     off_t actualStart;
228     int adjust;
229     void* memPtr;
230 
231     assert(pMap != NULL);
232 
233     if (getFileStartAndLength(fd, &dummy, &fileLength) < 0)
234         return -1;
235 
236     if (start + length > (long)fileLength) {
237         LOGW("bad segment: st=%d len=%ld flen=%d\n",
238             (int) start, length, (int) fileLength);
239         return -1;
240     }
241 
242     /* adjust to be page-aligned */
243     adjust = start % DEFAULT_PAGE_SIZE;
244     actualStart = start - adjust;
245     actualLength = length + adjust;
246 
247     memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
248                 fd, actualStart);
249     if (memPtr == MAP_FAILED) {
250         LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
251             (int) actualLength, fd, (int) actualStart, strerror(errno));
252         return -1;
253     }
254 
255     pMap->baseAddr = memPtr;
256     pMap->baseLength = actualLength;
257     pMap->addr = (char*)memPtr + adjust;
258     pMap->length = length;
259 
260     LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
261         (int) start, (int) length,
262         pMap->baseAddr, (int) pMap->baseLength,
263         pMap->addr, (int) pMap->length);
264 
265     return 0;
266 #else
267     LOGE("sysMapFileSegmentInShmem not implemented.\n");
268     return -1;
269 #endif
270 }
271 
272 /*
273  * Release a memory mapping.
274  */
sysReleaseShmem(MemMapping * pMap)275 void sysReleaseShmem(MemMapping* pMap)
276 {
277 #ifdef HAVE_POSIX_FILEMAP
278     if (pMap->baseAddr == NULL && pMap->baseLength == 0)
279         return;
280 
281     if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
282         LOGW("munmap(%p, %d) failed: %s\n",
283             pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
284     } else {
285         LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
286         pMap->baseAddr = NULL;
287         pMap->baseLength = 0;
288     }
289 #else
290     /* Free the bits allocated by sysMapFileInShmem. */
291     if (pMap->baseAddr != NULL) {
292       free(pMap->baseAddr);
293       pMap->baseAddr = NULL;
294     }
295     pMap->baseLength = 0;
296 #endif
297 }
298 
299 /*
300  * Make a copy of a MemMapping.
301  */
sysCopyMap(MemMapping * dst,const MemMapping * src)302 void sysCopyMap(MemMapping* dst, const MemMapping* src)
303 {
304     memcpy(dst, src, sizeof(MemMapping));
305 }
306 
307