2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
27 #ifdef HAVE_POSIX_FILEMAP
28 # include <sys/mman.h>
33 #include <JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd
37 * Create an anonymous shared memory segment large enough to hold "length"
38 * bytes. The actual segment may be larger because mmap() operates on
39 * page boundaries (usually 4K).
41 static void* sysCreateAnonShmem(size_t length)
43 #ifdef HAVE_POSIX_FILEMAP
46 ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
47 MAP_SHARED | MAP_ANON, -1, 0);
48 if (ptr == MAP_FAILED) {
49 LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
56 LOGE("sysCreateAnonShmem not implemented.\n");
62 * Create a private anonymous storage area.
64 int sysCreatePrivateMap(size_t length, MemMapping* pMap)
68 memPtr = sysCreateAnonShmem(length);
72 pMap->addr = pMap->baseAddr = memPtr;
73 pMap->length = pMap->baseLength = length;
78 * Determine the current offset and remaining length of the open file.
80 static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
85 assert(start_ != NULL);
86 assert(length_ != NULL);
88 start = lseek(fd, 0L, SEEK_CUR);
89 end = lseek(fd, 0L, SEEK_END);
90 (void) lseek(fd, start, SEEK_SET);
92 if (start == (off_t) -1 || end == (off_t) -1) {
93 LOGE("could not determine length of file\n");
99 LOGE("file is empty\n");
110 * Pull the contents of a file into an new shared memory segment. We grab
111 * everything from fd's current offset on.
113 * We need to know the length ahead of time so we can allocate a segment
114 * of sufficient size.
116 int sysLoadFileInShmem(int fd, MemMapping* pMap)
118 #ifdef HAVE_POSIX_FILEMAP
120 size_t length, actual;
123 assert(pMap != NULL);
125 if (getFileStartAndLength(fd, &start, &length) < 0)
128 memPtr = sysCreateAnonShmem(length);
132 actual = read(fd, memPtr, length);
133 if (actual != length) {
134 LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
135 sysReleaseShmem(pMap);
139 pMap->baseAddr = pMap->addr = memPtr;
140 pMap->baseLength = pMap->length = length;
144 LOGE("sysLoadFileInShmem not implemented.\n");
149 #ifndef HAVE_POSIX_FILEMAP
150 int sysFakeMapFile(int fd, MemMapping* pMap)
152 /* No MMAP, just fake it by copying the bits.
153 For Win32 we could use MapViewOfFile if really necessary
154 (see libs/utils/FileMap.cpp).
160 assert(pMap != NULL);
162 if (getFileStartAndLength(fd, &start, &length) < 0)
165 memPtr = malloc(length);
166 if (read(fd, memPtr, length) < 0) {
167 LOGW("read(fd=%d, start=%d, length=%d) failed: %s\n", (int) length,
168 fd, (int) start, strerror(errno));
172 pMap->baseAddr = pMap->addr = memPtr;
173 pMap->baseLength = pMap->length = length;
180 * Map a file (from fd's current offset) into a shared, read-only memory
181 * segment. The file offset must be a multiple of the system page size.
183 * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
184 * value and does not disturb "pMap".
186 int sysMapFileInShmemReadOnly(int fd, MemMapping* pMap)
188 #ifdef HAVE_POSIX_FILEMAP
193 assert(pMap != NULL);
195 if (getFileStartAndLength(fd, &start, &length) < 0)
198 memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
199 if (memPtr == MAP_FAILED) {
200 LOGW("mmap(%d, RO, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
201 fd, (int) start, strerror(errno));
205 pMap->baseAddr = pMap->addr = memPtr;
206 pMap->baseLength = pMap->length = length;
210 return sysFakeMapFile(fd, pMap);
215 * Map a file (from fd's current offset) into a private, read-write memory
216 * segment that will be marked read-only (a/k/a "writable read-only"). The
217 * file offset must be a multiple of the system page size.
219 * In some cases the mapping will be fully writable (e.g. for files on
222 * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
223 * value and does not disturb "pMap".
225 int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
227 #ifdef HAVE_POSIX_FILEMAP
232 assert(pMap != NULL);
234 if (getFileStartAndLength(fd, &start, &length) < 0)
237 memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE,
239 if (memPtr == MAP_FAILED) {
240 LOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s\n", (int) length,
241 fd, (int) start, strerror(errno));
244 if (mprotect(memPtr, length, PROT_READ) < 0) {
245 /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */
247 LOGV("mprotect(%p, %d, PROT_READ) failed: %s\n",
248 memPtr, length, strerror(err));
249 LOGD("mprotect(RO) failed (%d), file will remain read-write\n", err);
252 pMap->baseAddr = pMap->addr = memPtr;
253 pMap->baseLength = pMap->length = length;
257 return sysFakeMapFile(fd, pMap);
262 * Map part of a file into a shared, read-only memory segment. The "start"
263 * offset is absolute, not relative.
265 * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
266 * value and does not disturb "pMap".
268 int sysMapFileSegmentInShmem(int fd, off_t start, size_t length,
271 #ifdef HAVE_POSIX_FILEMAP
277 assert(pMap != NULL);
279 /* adjust to be page-aligned */
280 adjust = start % SYSTEM_PAGE_SIZE;
281 actualStart = start - adjust;
282 actualLength = length + adjust;
284 memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
286 if (memPtr == MAP_FAILED) {
287 LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
288 (int) actualLength, fd, (int) actualStart, strerror(errno));
292 pMap->baseAddr = memPtr;
293 pMap->baseLength = actualLength;
294 pMap->addr = (char*)memPtr + adjust;
295 pMap->length = length;
297 LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
298 (int) start, (int) length,
299 pMap->baseAddr, (int) pMap->baseLength,
300 pMap->addr, (int) pMap->length);
304 LOGE("sysMapFileSegmentInShmem not implemented.\n");
310 * Change the access rights on one or more pages to read-only or read-write.
312 * Returns 0 on success.
314 int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
317 #ifdef HAVE_POSIX_FILEMAP
319 * Verify that "addr" is part of this mapping file.
321 if (addr < pMap->baseAddr ||
322 (u1*)addr >= (u1*)pMap->baseAddr + pMap->baseLength)
324 LOGE("Attempted to change %p; map is %p - %p\n",
325 addr, pMap->baseAddr, (u1*)pMap->baseAddr + pMap->baseLength);
330 * Align "addr" to a page boundary and adjust "length" appropriately.
331 * (The address must be page-aligned, the length doesn't need to be,
332 * but we do need to ensure we cover the same range.)
334 u1* alignAddr = (u1*) ((int) addr & ~(SYSTEM_PAGE_SIZE-1));
335 size_t alignLength = length + ((u1*) addr - alignAddr);
337 //LOGI("%p/%zd --> %p/%zd\n", addr, length, alignAddr, alignLength);
338 int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ);
339 if (mprotect(alignAddr, alignLength, prot) != 0) {
341 LOGV("mprotect (%p,%zd,%d) failed: %s\n",
342 alignAddr, alignLength, prot, strerror(errno));
343 return (errno != 0) ? errno : -1;
347 /* for "fake" mapping, no need to do anything */
352 * Release a memory mapping.
354 void sysReleaseShmem(MemMapping* pMap)
356 #ifdef HAVE_POSIX_FILEMAP
357 if (pMap->baseAddr == NULL && pMap->baseLength == 0)
360 if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
361 LOGW("munmap(%p, %d) failed: %s\n",
362 pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
364 LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
365 pMap->baseAddr = NULL;
366 pMap->baseLength = 0;
369 /* Free the bits allocated by sysMapFileInShmem. */
370 if (pMap->baseAddr != NULL) {
371 free(pMap->baseAddr);
372 pMap->baseAddr = NULL;
374 pMap->baseLength = 0;
379 * Make a copy of a MemMapping.
381 void sysCopyMap(MemMapping* dst, const MemMapping* src)
383 memcpy(dst, src, sizeof(MemMapping));
387 * Write until all bytes have been written.
389 * Returns 0 on success, or an errno value on failure.
391 int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg)
394 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
397 LOGE("%s: write failed: %s\n", logMsg, strerror(err));
399 } else if (actual != (ssize_t) count) {
400 LOGD("%s: partial write (will retry): (%d of %zd)\n",
401 logMsg, (int) actual, count);
402 buf = (const void*) (((const u1*) buf) + actual);