From f4115eb2c36bac741685300273cb9fa186d719e1 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Tue, 15 Feb 2022 11:03:59 -0800 Subject: [PATCH] Implement MAP_ANONYMOUS on top of malloc in STANDALONE_WASM mode (#16289) This hack already exists in the JS implementation of mmap. We could try to unify the two but I'm not sure its worth it. Fixes: #16280, #13312 --- system/lib/standalone/standalone.c | 70 ++++++++++++++++++++++++++++-- tests/core/test_mmap.c | 9 +++- tests/test_core.py | 3 +- 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/system/lib/standalone/standalone.c b/system/lib/standalone/standalone.c index 227136d81fdf..dc0d72ef3149 100644 --- a/system/lib/standalone/standalone.c +++ b/system/lib/standalone/standalone.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include @@ -17,6 +19,8 @@ #include #include +#include "lock.h" + /* * WASI support code. These are compiled with the program, and call out * using wasi APIs, which can be provided either by a wasi VM or by our @@ -59,15 +63,73 @@ int clock_getres(clockid_t clk_id, struct timespec *tp) { // mmap support is nonexistent. TODO: emulate simple mmaps using // stdio + malloc, which is slow but may help some things? -long __map_file(int x, int y) { - return -ENOSYS; +const unsigned char * __map_file(const char *pathname, size_t *size) { + errno = ENOSYS; + return NULL; } -long __syscall_munmap(int x, int y) { - return -ENOSYS; +struct map { + void* addr; + long length; + struct map* next; +} __attribute__((aligned (1))); + +static volatile int lock[1]; +static struct map* mappings; + +long __syscall_munmap(long addr, long length) { + LOCK(lock); + struct map* map = mappings; + struct map* prev = NULL; + while (map) { + if (map->addr == (void*)addr) { + // We don't support partial munmapping. + if (map->length != length) { + map = NULL; + break; + } + if (prev) { + prev->next = map->next; + } else { + mappings = map->next; + } + break; + } + prev = map; + map = map->next; + } + UNLOCK(lock); + + if (map) { + // Release the memory. + free(map->addr); + // Success! + return 0; + } + + errno = EINVAL; + return -1; } long __syscall_mmap2(long addr, long len, long prot, long flags, long fd, long off) { + // MAP_ANONYMOUS (aka MAP_ANON) isn't actually defined by POSIX spec, + // but it is widely used way to allocate memory pages on Linux, BSD and Mac. + // In this case fd argument is ignored. + if (flags & MAP_ANONYMOUS) { + void* ptr = memalign(WASM_PAGE_SIZE, len + sizeof(struct map)); + if (!ptr) { + return -ENOMEM; + } + memset(ptr, 0, len); + struct map* new_map = (struct map*)((char*)ptr + len); + new_map->addr = ptr; + new_map->length = len; + LOCK(lock); + new_map->next = mappings; + mappings = new_map; + UNLOCK(lock); + return (long)ptr; + } return -ENOSYS; } diff --git a/tests/core/test_mmap.c b/tests/core/test_mmap.c index e091892537e7..aac00310ff47 100644 --- a/tests/core/test_mmap.c +++ b/tests/core/test_mmap.c @@ -17,12 +17,17 @@ int main(int argc, char* argv[]) { assert(getpagesize() == 65536); assert(sysconf(_SC_PAGESIZE) == 65536); + int* maps[10]; for (int i = 0; i < 10; i++) { int* map = (int*)mmap(0, 5000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); assert(map != MAP_FAILED); assert(((long)map) % 65536 == 0); // aligned - assert(munmap(map, 5000) == 0); + maps[i] = map; + } + + for (int i = 0; i < 10; i++) { + assert(munmap(maps[i], 5000) == 0); } const int NUM_BYTES = 8 * 1024 * 1024; @@ -44,6 +49,6 @@ int main(int argc, char* argv[]) { assert(munmap(map, NUM_BYTES) == 0); - printf("hello,world"); + printf("hello,world\n"); return 0; } diff --git a/tests/test_core.py b/tests/test_core.py index ec24f1aa49a9..66cdd58e52ea 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6061,13 +6061,12 @@ def test_static_variable(self): def test_fakestat(self): self.do_core_test('test_fakestat.c') + @also_with_standalone_wasm() def test_mmap(self): # ASan needs more memory, but that is set up separately if '-fsanitize=address' not in self.emcc_args: self.set_setting('INITIAL_MEMORY', '128mb') - # needs to flush stdio streams - self.set_setting('EXIT_RUNTIME') self.do_core_test('test_mmap.c') def test_mmap_file(self):