]>
Commit | Line | Data |
---|---|---|
ca9046d8 DG |
1 | // Userspace emulation of mmap and munmap. Restrictions apply. |
2 | // | |
3 | // This is meant to be complete enough to be compatible with code that uses | |
4 | // mmap for simple file I/O. It just allocates memory with malloc and reads | |
5 | // and writes data with pread and pwrite. | |
320054e8 DG |
6 | |
7 | #define _WASI_EMULATED_MMAN | |
8 | #include <stdlib.h> | |
9 | #include <errno.h> | |
10 | #include <unistd.h> | |
11 | #include <string.h> | |
12 | #include <sys/mman.h> | |
13 | #include <sys/types.h> | |
14 | ||
15 | struct map { | |
16 | int prot; | |
17 | int flags; | |
18 | off_t offset; | |
19 | size_t length; | |
320054e8 DG |
20 | }; |
21 | ||
22 | void *mmap(void *addr, size_t length, int prot, int flags, | |
23 | int fd, off_t offset) { | |
24 | // Check for unsupported flags. | |
3e9892fc | 25 | if ((flags & (MAP_PRIVATE | MAP_SHARED)) == 0 || |
26 | (flags & MAP_FIXED) != 0 || | |
320054e8 | 27 | #ifdef MAP_SHARED_VALIDATE |
3e9892fc | 28 | (flags & MAP_SHARED_VALIDATE) == MAP_SHARED_VALIDATE || |
320054e8 DG |
29 | #endif |
30 | #ifdef MAP_NORESERVE | |
31 | (flags & MAP_NORESERVE) != 0 || | |
32 | #endif | |
33 | #ifdef MAP_GROWSDOWN | |
34 | (flags & MAP_GROWSDOWN) != 0 || | |
35 | #endif | |
36 | #ifdef MAP_HUGETLB | |
37 | (flags & MAP_HUGETLB) != 0 || | |
38 | #endif | |
39 | #ifdef MAP_FIXED_NOREPLACE | |
40 | (flags & MAP_FIXED_NOREPLACE) != 0 || | |
41 | #endif | |
42 | 0) | |
43 | { | |
44 | errno = EINVAL; | |
45 | return MAP_FAILED; | |
46 | } | |
47 | ||
48 | // Check for unsupported protection requests. | |
49 | if (prot == PROT_NONE || | |
50 | #ifdef PROT_EXEC | |
51 | (prot & PROT_EXEC) != 0 || | |
52 | #endif | |
53 | 0) | |
54 | { | |
55 | errno = EINVAL; | |
56 | return MAP_FAILED; | |
57 | } | |
58 | ||
d987aad4 | 59 | // To be consistent with POSIX. |
60 | if (length == 0) { | |
61 | errno = EINVAL; | |
62 | return MAP_FAILED; | |
63 | } | |
64 | ||
4a8d1c7c | 65 | // Check for integer overflow. |
30e5a1fd | 66 | size_t buf_len = 0; |
d987aad4 | 67 | if (__builtin_add_overflow(length, sizeof(struct map), &buf_len)) { |
68 | errno = ENOMEM; | |
20c48b57 | 69 | return MAP_FAILED; |
70 | } | |
71 | ||
320054e8 | 72 | // Allocate the memory. |
ccfe4bda | 73 | struct map *map = malloc(buf_len); |
320054e8 DG |
74 | if (!map) { |
75 | errno = ENOMEM; | |
76 | return MAP_FAILED; | |
77 | } | |
78 | ||
79 | // Initialize the header. | |
80 | map->prot = prot; | |
81 | map->flags = flags; | |
82 | map->offset = offset; | |
83 | map->length = length; | |
84 | ||
85 | // Initialize the main memory buffer, either with the contents of a file, | |
86 | // or with zeros. | |
9b9d243b | 87 | addr = map + 1; |
320054e8 | 88 | if ((flags & MAP_ANON) == 0) { |
9b9d243b | 89 | char *body = (char *)addr; |
320054e8 | 90 | while (length > 0) { |
20c48b57 | 91 | const ssize_t nread = pread(fd, body, length, offset); |
320054e8 DG |
92 | if (nread < 0) { |
93 | if (errno == EINTR) | |
94 | continue; | |
95 | return MAP_FAILED; | |
96 | } | |
97 | if (nread == 0) | |
98 | break; | |
99 | length -= (size_t)nread; | |
100 | offset += (size_t)nread; | |
101 | body += (size_t)nread; | |
102 | } | |
103 | } else { | |
9b9d243b | 104 | memset(addr, 0, length); |
320054e8 DG |
105 | } |
106 | ||
9b9d243b | 107 | return addr; |
320054e8 DG |
108 | } |
109 | ||
110 | int munmap(void *addr, size_t length) { | |
111 | struct map *map = (struct map *)addr - 1; | |
ca9046d8 | 112 | |
320054e8 DG |
113 | // We don't support partial munmapping. |
114 | if (map->length != length) { | |
115 | errno = EINVAL; | |
116 | return -1; | |
117 | } | |
118 | ||
119 | // Release the memory. | |
120 | free(map); | |
121 | ||
122 | // Success! | |
123 | return 0; | |
124 | } |