summaryrefslogtreecommitdiff
path: root/linux-user/mmap.c
diff options
context:
space:
mode:
authoredgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>2009-02-03 23:06:34 +0000
committeredgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>2009-02-03 23:06:34 +0000
commit54c5a2ae54aa02a3e7a1f708c20b6bffe81b330b (patch)
tree57fbc2239482a8e1a72022093f706705f2c6c052 /linux-user/mmap.c
parentd6755878dbc4278c281f88905a7c02028c5b69c5 (diff)
downloadqemu-54c5a2ae54aa02a3e7a1f708c20b6bffe81b330b.tar.gz
Partialy fix mmap at EOF for large pagesize targets in user-mode.
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6510 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'linux-user/mmap.c')
-rw-r--r--linux-user/mmap.c39
1 files changed, 37 insertions, 2 deletions
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 888b491403..6f300a0400 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -24,6 +24,8 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <sys/mman.h>
#include <linux/mman.h>
#include <linux/unistd.h>
@@ -366,6 +368,36 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
goto the_end;
real_start = start & qemu_host_page_mask;
+ /* When mapping files into a memory area larger than the file, accesses
+ to pages beyond the file size will cause a SIGBUS.
+
+ For example, if mmaping a file of 100 bytes on a host with 4K pages
+ emulating a target with 8K pages, the target expects to be able to
+ access the first 8K. But the host will trap us on any access beyond
+ 4K.
+
+ When emulating a target with a larger page-size than the hosts, we
+ may need to truncate file maps at EOF and add extra anonymous pages
+ up to the targets page boundary. */
+
+ if ((qemu_real_host_page_size < TARGET_PAGE_SIZE)
+ && !(flags & MAP_ANONYMOUS)) {
+ struct stat sb;
+
+ if (fstat (fd, &sb) == -1)
+ goto fail;
+
+ /* Are we trying to create a map beyond EOF?. */
+ if (offset + len > sb.st_size) {
+ /* If so, truncate the file map at eof aligned with
+ the hosts real pagesize. Additional anonymous maps
+ will be created beyond EOF. */
+ len = (sb.st_size - offset);
+ len += qemu_real_host_page_size - 1;
+ len &= ~(qemu_real_host_page_size - 1);
+ }
+ }
+
if (!(flags & MAP_FIXED)) {
abi_ulong mmap_start;
void *p;
@@ -381,13 +413,16 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
especially important if qemu_host_page_size >
qemu_real_host_page_size */
p = mmap(g2h(mmap_start),
- host_len, prot, flags | MAP_FIXED, fd, host_offset);
+ host_len, prot, flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
if (p == MAP_FAILED)
goto fail;
/* update start so that it points to the file position at 'offset' */
host_start = (unsigned long)p;
- if (!(flags & MAP_ANONYMOUS))
+ if (!(flags & MAP_ANONYMOUS)) {
+ p = mmap(g2h(mmap_start), len, prot,
+ flags | MAP_FIXED, fd, host_offset);
host_start += offset - host_offset;
+ }
start = h2g(host_start);
} else {
int flg;