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