Emscripten does not support partial unmapping of mmapped memory regions[1]. This limitation prevents correct implementation of qemu_ram_mmap and qemu_ram_munmap, which rely on partial unmap behavior.
As a workaround, this commit excludes mmap-alloc.c from the Emscripten build. Instead, for Emscripten build, this modifies qemu_anon_ram_alloc to use qemu_memalign in place of qemu_ram_mmap, and disable memory backends that rely on mmap, such as memory-backend-file and memory-backend-shm. [1] https://github.com/emscripten-core/emscripten/blob/d4a74336f23214bf3304d9eb0d03966786b30a36/system/lib/libc/emscripten_mmap.c#L61 Signed-off-by: Kohei Tokunaga <ktokunaga.m...@gmail.com> --- backends/meson.build | 6 ++++-- system/memory.c | 2 +- system/physmem.c | 9 +++++---- util/meson.build | 4 +++- util/oslib-posix.c | 28 ++++++++++++++++++++++++++++ 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/backends/meson.build b/backends/meson.build index da714b93d1..9b88d22685 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -12,8 +12,10 @@ system_ss.add([files( if host_os != 'windows' system_ss.add(files('rng-random.c')) - system_ss.add(files('hostmem-file.c')) - system_ss.add([files('hostmem-shm.c'), rt]) + if host_os != 'emscripten' + system_ss.add(files('hostmem-file.c')) + system_ss.add([files('hostmem-shm.c'), rt]) + endif endif if host_os == 'linux' system_ss.add(files('hostmem-memfd.c')) diff --git a/system/memory.c b/system/memory.c index 7e2f16f4e9..f76b895950 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1627,7 +1627,7 @@ bool memory_region_init_resizeable_ram(MemoryRegion *mr, return true; } -#ifdef CONFIG_POSIX +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) bool memory_region_init_ram_from_file(MemoryRegion *mr, Object *owner, const char *name, diff --git a/system/physmem.c b/system/physmem.c index 16cf557d1a..4a7a2e5e12 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1243,7 +1243,7 @@ long qemu_maxrampagesize(void) return pagesize; } -#ifdef CONFIG_POSIX +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) static int64_t get_file_size(int fd) { int64_t size; @@ -1978,7 +1978,7 @@ out_free: } } -#ifdef CONFIG_POSIX +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, ram_addr_t max_size, qemu_ram_resize_cb resized, MemoryRegion *mr, uint32_t ram_flags, int fd, off_t offset, @@ -2158,7 +2158,8 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, assert(!host ^ (ram_flags & RAM_PREALLOC)); assert(max_size >= size); -#ifdef CONFIG_POSIX /* ignore RAM_SHARED for Windows */ + /* ignore RAM_SHARED for Windows and emscripten*/ +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) if (!host) { if (!share_flags && current_machine->aux_ram_share) { ram_flags |= RAM_SHARED; @@ -2255,7 +2256,7 @@ static void reclaim_ramblock(RAMBlock *block) ; } else if (xen_enabled()) { xen_invalidate_map_cache_entry(block->host); -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(EMSCRIPTEN) } else if (block->fd >= 0) { qemu_ram_munmap(block->fd, block->host, block->max_length); close(block->fd); diff --git a/util/meson.build b/util/meson.build index 780b5977a8..e5cd327e27 100644 --- a/util/meson.build +++ b/util/meson.build @@ -11,7 +11,9 @@ if host_os != 'windows' endif util_ss.add(files('compatfd.c')) util_ss.add(files('event_notifier-posix.c')) - util_ss.add(files('mmap-alloc.c')) + if host_os != 'emscripten' + util_ss.add(files('mmap-alloc.c')) + endif freebsd_dep = [] if host_os == 'freebsd' freebsd_dep = util diff --git a/util/oslib-posix.c b/util/oslib-posix.c index a697c602c6..4ff577e5de 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -58,6 +58,7 @@ #include <lwp.h> #endif +#include "qemu/memalign.h" #include "qemu/mmap-alloc.h" #define MAX_MEM_PREALLOC_THREAD_COUNT 16 @@ -210,11 +211,21 @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared, const uint32_t qemu_map_flags = (shared ? QEMU_MAP_SHARED : 0) | (noreserve ? QEMU_MAP_NORESERVE : 0); size_t align = QEMU_VMALLOC_ALIGN; +#ifndef EMSCRIPTEN void *ptr = qemu_ram_mmap(-1, size, align, qemu_map_flags, 0); if (ptr == MAP_FAILED) { return NULL; } +#else + /* + * qemu_ram_mmap is not implemented for Emscripten. Use qemu_memalign + * for the anonymous allocation. noreserve is ignored as there is no swap + * space on Emscripten, and shared is ignored as there is no other + * processes on Emscripten. + */ + void *ptr = qemu_memalign(align, size); +#endif if (alignment) { *alignment = align; @@ -227,7 +238,16 @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared, void qemu_anon_ram_free(void *ptr, size_t size) { trace_qemu_anon_ram_free(ptr, size); +#ifndef EMSCRIPTEN qemu_ram_munmap(-1, ptr, size); +#else + /* + * qemu_ram_munmap is not implemented for Emscripten and qemu_memalign + * was used for the allocation. Use the corresponding freeing function + * here. + */ + qemu_vfree(ptr); +#endif } void qemu_socket_set_block(int fd) @@ -588,7 +608,15 @@ bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, { static gsize initialized; int ret; +#ifndef EMSCRIPTEN size_t hpagesize = qemu_fd_getpagesize(fd); +#else + /* + * mmap-alloc.c is excluded from Emscripten build, so qemu_fd_getpagesize + * is unavailable. Fallback to the lower level implementation. + */ + size_t hpagesize = qemu_real_host_page_size(); +#endif size_t numpages = DIV_ROUND_UP(sz, hpagesize); bool use_madv_populate_write; struct sigaction act; -- 2.43.0