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 * System utilities.
18 */
19 #include "DexFile.h"
20 #include "SysUtil.h"
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <string.h>
26
27 #ifdef HAVE_POSIX_FILEMAP
28 #include <sys/mman.h>
29 #endif
30
31 #include <limits.h>
32 #include <errno.h>
33
34 /*
35 * Having trouble finding a portable way to get this. sysconf(_SC_PAGE_SIZE)
36 * seems appropriate, but we don't have that on the device. Some systems
37 * have getpagesize(2), though the linux man page has some odd cautions.
38 */
39 #define DEFAULT_PAGE_SIZE 4096
40
41
42 /*
43 * Create an anonymous shared memory segment large enough to hold "length"
44 * bytes. The actual segment may be larger because mmap() operates on
45 * page boundaries (usually 4K).
46 */
sysCreateAnonShmem(size_t length)47 static void* sysCreateAnonShmem(size_t length)
48 {
49 #ifdef HAVE_POSIX_FILEMAP
50 void* ptr;
51
52 ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
53 MAP_SHARED | MAP_ANON, -1, 0);
54 if (ptr == MAP_FAILED) {
55 LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
56 strerror(errno));
57 return NULL;
58 }
59
60 return ptr;
61 #else
62 LOGE("sysCreateAnonShmem not implemented.\n");
63 return NULL;
64 #endif
65 }
66
getFileStartAndLength(int fd,off_t * start_,size_t * length_)67 static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
68 {
69 off_t start, end;
70 size_t length;
71
72 assert(start_ != NULL);
73 assert(length_ != NULL);
74
75 start = lseek(fd, 0L, SEEK_CUR);
76 end = lseek(fd, 0L, SEEK_END);
77 (void) lseek(fd, start, SEEK_SET);
78
79 if (start == (off_t) -1 || end == (off_t) -1) {
80 LOGE("could not determine length of file\n");
81 return -1;
82 }
83
84 length = end - start;
85 if (length == 0) {
86 LOGE("file is empty\n");
87 return -1;
88 }
89
90 *start_ = start;
91 *length_ = length;
92
93 return 0;
94 }
95
96 /*
97 * Pull the contents of a file into an new shared memory segment. We grab
98 * everything from fd's current offset on.
99 *
100 * We need to know the length ahead of time so we can allocate a segment
101 * of sufficient size.
102 */
sysLoadFileInShmem(int fd,MemMapping * pMap)103 int sysLoadFileInShmem(int fd, MemMapping* pMap)
104 {
105 #ifdef HAVE_POSIX_FILEMAP
106 off_t start;
107 size_t length, actual;
108 void* memPtr;
109
110 assert(pMap != NULL);
111
112 if (getFileStartAndLength(fd, &start, &length) < 0)
113 return -1;
114
115 memPtr = sysCreateAnonShmem(length);
116 if (memPtr == NULL)
117 return -1;
118
119 actual = read(fd, memPtr, length);
120 if (actual != length) {
121 LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
122 sysReleaseShmem(pMap);
123 return -1;
124 }
125
126 pMap->baseAddr = pMap->addr = memPtr;
127 pMap->baseLength = pMap->length = length;
128
129 return 0;
130 #else
131 LOGE("sysLoadFileInShmem not implemented.\n");
132 return -1;
133 #endif
134 }
135
136 /*
137 * Map a file (from fd's current offset) into a shared, read-only memory
138 * segment. The file offset must be a multiple of the page size.
139 *
140 * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
141 * value and does not disturb "pMap".
142 */
sysMapFileInShmem(int fd,MemMapping * pMap)143 int sysMapFileInShmem(int fd, MemMapping* pMap)
144 {
145 #ifdef HAVE_POSIX_FILEMAP
146 off_t start;
147 size_t length;
148 void* memPtr;
149
150 assert(pMap != NULL);
151
152 if (getFileStartAndLength(fd, &start, &length) < 0)
153 return -1;
154
155 memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
156 if (memPtr == MAP_FAILED) {
157 LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
158 fd, (int) start, strerror(errno));
159 return -1;
160 }
161
162 pMap->baseAddr = pMap->addr = memPtr;
163 pMap->baseLength = pMap->length = length;
164
165 return 0;
166 #else
167 /* No MMAP, just fake it by copying the bits.
168 For Win32 we could use MapViewOfFile if really necessary
169 (see libs/utils/FileMap.cpp).
170 */
171 off_t start;
172 size_t length;
173 void* memPtr;
174
175 assert(pMap != NULL);
176
177 if (getFileStartAndLength(fd, &start, &length) < 0)
178 return -1;
179
180 memPtr = malloc(length);
181 if (read(fd, memPtr, length) < 0) {
182 LOGW("read(fd=%d, start=%d, length=%d) failed: %s\n", (int) length,
183 fd, (int) start, strerror(errno));
184 return -1;
185 }
186
187 pMap->baseAddr = pMap->addr = memPtr;
188 pMap->baseLength = pMap->length = length;
189
190 return 0;
191 #endif
192 }
193
194 /*
195 * Map part of a file (from fd's current offset) into a shared, read-only
196 * memory segment.
197 *
198 * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
199 * value and does not disturb "pMap".
200 */
sysMapFileSegmentInShmem(int fd,off_t start,long length,MemMapping * pMap)201 int sysMapFileSegmentInShmem(int fd, off_t start, long length,
202 MemMapping* pMap)
203 {
204 #ifdef HAVE_POSIX_FILEMAP
205 off_t dummy;
206 size_t fileLength, actualLength;
207 off_t actualStart;
208 int adjust;
209 void* memPtr;
210
211 assert(pMap != NULL);
212
213 if (getFileStartAndLength(fd, &dummy, &fileLength) < 0)
214 return -1;
215
216 if (start + length > (long)fileLength) {
217 LOGW("bad segment: st=%d len=%ld flen=%d\n",
218 (int) start, length, (int) fileLength);
219 return -1;
220 }
221
222 /* adjust to be page-aligned */
223 adjust = start % DEFAULT_PAGE_SIZE;
224 actualStart = start - adjust;
225 actualLength = length + adjust;
226
227 memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
228 fd, actualStart);
229 if (memPtr == MAP_FAILED) {
230 LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
231 (int) actualLength, fd, (int) actualStart, strerror(errno));
232 return -1;
233 }
234
235 pMap->baseAddr = memPtr;
236 pMap->baseLength = actualLength;
237 pMap->addr = (char*)memPtr + adjust;
238 pMap->length = length;
239
240 LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
241 (int) start, (int) length,
242 pMap->baseAddr, (int) pMap->baseLength,
243 pMap->addr, (int) pMap->length);
244
245 return 0;
246 #else
247 LOGE("sysMapFileSegmentInShmem not implemented.\n");
248 return -1;
249 #endif
250 }
251
252 /*
253 * Release a memory mapping.
254 */
sysReleaseShmem(MemMapping * pMap)255 void sysReleaseShmem(MemMapping* pMap)
256 {
257 #ifdef HAVE_POSIX_FILEMAP
258 if (pMap->baseAddr == NULL && pMap->baseLength == 0)
259 return;
260
261 if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
262 LOGW("munmap(%p, %d) failed: %s\n",
263 pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
264 } else {
265 LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
266 pMap->baseAddr = NULL;
267 pMap->baseLength = 0;
268 }
269 #else
270 /* Free the bits allocated by sysMapFileInShmem. */
271 if (pMap->baseAddr != NULL) {
272 free(pMap->baseAddr);
273 pMap->baseAddr = NULL;
274 }
275 pMap->baseLength = 0;
276 #endif
277 }
278
279 /*
280 * Make a copy of a MemMapping.
281 */
sysCopyMap(MemMapping * dst,const MemMapping * src)282 void sysCopyMap(MemMapping* dst, const MemMapping* src)
283 {
284 memcpy(dst, src, sizeof(MemMapping));
285 }
286
287