1 // Based on
2 // https://web.archive.org/web/20191012035921/http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system#BSD
3 // Check for any posix or unix OS
4 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || \
5 (defined(__APPLE__) && defined(__MACH__)))
6
7 #include "wasm-rt.h"
8 #include "wasm-rt-os.h"
9
10 #include <errno.h>
11 #include <inttypes.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #if defined(__APPLE__) && defined(__MACH__)
17 // Macs priors to OSX 10.12 don't have the clock functions. So we will use mac specific options
18 #include <sys/time.h>
19 #include <mach/mach_time.h>
20 #endif
21 #include <sys/mman.h>
22 #include <unistd.h>
23
24 #ifdef VERBOSE_LOGGING
25 #define VERBOSE_LOG(...) { printf(__VA_ARGS__); }
26 #else
27 #define VERBOSE_LOG(...)
28 #endif
29
os_getpagesize()30 size_t os_getpagesize() {
31 return getpagesize();
32 }
33
os_mmap(void * hint,size_t size,int prot,int flags)34 void* os_mmap(void* hint, size_t size, int prot, int flags) {
35 int map_prot = PROT_NONE;
36 int map_flags = MAP_ANONYMOUS | MAP_PRIVATE;
37 uint64_t request_size, page_size;
38 uint8_t* addr;
39
40 page_size = (uint64_t)os_getpagesize();
41 request_size = (size + page_size - 1) & ~(page_size - 1);
42
43 if ((size_t)request_size < size)
44 /* integer overflow */
45 return NULL;
46
47 if (request_size > 16 * (uint64_t)UINT32_MAX)
48 /* At most 16 G is allowed */
49 return NULL;
50
51 if (prot & MMAP_PROT_READ)
52 map_prot |= PROT_READ;
53
54 if (prot & MMAP_PROT_WRITE)
55 map_prot |= PROT_WRITE;
56
57 if (prot & MMAP_PROT_EXEC)
58 map_prot |= PROT_EXEC;
59
60 #if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64)
61 #ifndef __APPLE__
62 if (flags & MMAP_MAP_32BIT)
63 map_flags |= MAP_32BIT;
64 #endif
65 #endif
66
67 if (flags & MMAP_MAP_FIXED)
68 map_flags |= MAP_FIXED;
69
70 addr = mmap(hint, request_size, map_prot, map_flags, -1, 0);
71
72 if (addr == MAP_FAILED)
73 return NULL;
74
75 return addr;
76 }
77
os_munmap(void * addr,size_t size)78 void os_munmap(void* addr, size_t size) {
79 uint64_t page_size = (uint64_t)os_getpagesize();
80 uint64_t request_size = (size + page_size - 1) & ~(page_size - 1);
81
82 if (addr) {
83 if (munmap(addr, request_size)) {
84 printf("os_munmap error addr:%p, size:0x%" PRIx64 ", errno:%d\n", addr,
85 request_size, errno);
86 }
87 }
88 }
89
os_mprotect(void * addr,size_t size,int prot)90 int os_mprotect(void* addr, size_t size, int prot) {
91 int map_prot = PROT_NONE;
92 uint64_t page_size = (uint64_t)os_getpagesize();
93 uint64_t request_size = (size + page_size - 1) & ~(page_size - 1);
94
95 if (!addr)
96 return 0;
97
98 if (prot & MMAP_PROT_READ)
99 map_prot |= PROT_READ;
100
101 if (prot & MMAP_PROT_WRITE)
102 map_prot |= PROT_WRITE;
103
104 if (prot & MMAP_PROT_EXEC)
105 map_prot |= PROT_EXEC;
106
107 return mprotect(addr, request_size, map_prot);
108 }
109
os_mmap_aligned(void * addr,size_t requested_length,int prot,int flags,size_t alignment,size_t alignment_offset)110 void* os_mmap_aligned(void* addr,
111 size_t requested_length,
112 int prot,
113 int flags,
114 size_t alignment,
115 size_t alignment_offset) {
116 size_t padded_length = requested_length + alignment + alignment_offset;
117 uintptr_t unaligned = (uintptr_t)os_mmap(addr, padded_length, prot, flags);
118
119 VERBOSE_LOG("os_mmap_aligned: alignment:%llu, alignment_offset:%llu, requested_length:%llu, padded_length: %llu, initial mapping: %p\n",
120 (unsigned long long) alignment,
121 (unsigned long long) alignment_offset,
122 (unsigned long long) requested_length,
123 (unsigned long long) padded_length,
124 (void*) unaligned);
125
126 if (!unaligned) {
127 return (void*)unaligned;
128 }
129
130 // Round up the next address that has addr % alignment = 0
131 const size_t alignment_corrected = alignment == 0? 1 : alignment;
132 uintptr_t aligned_nonoffset =
133 (unaligned + (alignment_corrected - 1)) & ~(alignment_corrected - 1);
134
135 // Currently offset 0 is aligned according to alignment
136 // Alignment needs to be enforced at the given offset
137 uintptr_t aligned = 0;
138 if ((aligned_nonoffset - alignment_offset) >= unaligned) {
139 aligned = aligned_nonoffset - alignment_offset;
140 } else {
141 aligned = aligned_nonoffset - alignment_offset + alignment;
142 }
143
144 // Sanity check
145 if (aligned < unaligned ||
146 (aligned + (requested_length - 1)) > (unaligned + (padded_length - 1)) ||
147 (aligned + alignment_offset) % alignment_corrected != 0) {
148 VERBOSE_LOG("os_mmap_aligned: sanity check fail. aligned: %p\n", (void*) aligned);
149 os_munmap((void*)unaligned, padded_length);
150 return NULL;
151 }
152
153 {
154 size_t unused_front = aligned - unaligned;
155 if (unused_front != 0) {
156 os_munmap((void*)unaligned, unused_front);
157 }
158 }
159
160 {
161 size_t unused_back =
162 (unaligned + (padded_length - 1)) - (aligned + (requested_length - 1));
163 if (unused_back != 0) {
164 os_munmap((void*)(aligned + requested_length), unused_back);
165 }
166 }
167
168 VERBOSE_LOG("os_mmap_aligned: final mapping: %p\n", (void*) aligned);
169 return (void*)aligned;
170 }
171
os_mmap_commit(void * curr_heap_end_pointer,size_t expanded_size,int prot)172 int os_mmap_commit(void* curr_heap_end_pointer, size_t expanded_size, int prot) {
173 return os_mprotect(curr_heap_end_pointer, expanded_size, prot);
174 }
175
176 #if defined(__APPLE__) && defined(__MACH__)
177 typedef struct {
178 mach_timebase_info_data_t timebase; /* numer = 0, denom = 0 */
179 struct timespec inittime; /* nanoseconds since 1-Jan-1970 to init() */
180 uint64_t initclock; /* ticks since boot to init() */
181 } wasi_mac_clock_info_t;
182
183 static wasi_mac_clock_info_t g_wasi_mac_clock_info;
184 static int g_os_data_initialized = 0;
185 #endif
186
187
os_init()188 void os_init() {
189 #if defined(__APPLE__) && defined(__MACH__)
190 // From here: https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x/21352348#21352348
191 if (mach_timebase_info(&g_wasi_mac_clock_info.timebase) != 0) {
192 wasm_rt_trap(WASM_RT_TRAP_WASI);
193 }
194
195 // microseconds since 1 Jan 1970
196 struct timeval micro;
197 if (gettimeofday(µ, NULL) != 0) {
198 wasm_rt_trap(WASM_RT_TRAP_WASI);
199 }
200
201 g_wasi_mac_clock_info.initclock = mach_absolute_time();
202
203 g_wasi_mac_clock_info.inittime.tv_sec = micro.tv_sec;
204 g_wasi_mac_clock_info.inittime.tv_nsec = micro.tv_usec * 1000;
205
206 g_os_data_initialized = 1;
207 #endif
208 }
209
os_clock_init(void ** clock_data_pointer)210 void os_clock_init(void** clock_data_pointer) {
211 #if defined(__APPLE__) && defined(__MACH__)
212 if (!g_os_data_initialized) {
213 os_init();
214 }
215
216 wasi_mac_clock_info_t* alloc = (wasi_mac_clock_info_t*) malloc(sizeof(wasi_mac_clock_info_t));
217 if (!alloc) {
218 wasm_rt_trap(WASM_RT_TRAP_WASI);
219 }
220 memcpy(alloc, &g_wasi_mac_clock_info, sizeof(wasi_mac_clock_info_t));
221 *clock_data_pointer = alloc;
222 #endif
223 }
224
os_clock_cleanup(void ** clock_data_pointer)225 void os_clock_cleanup(void** clock_data_pointer) {
226 #if defined(__APPLE__) && defined(__MACH__)
227 if (*clock_data_pointer == 0) {
228 free(*clock_data_pointer);
229 *clock_data_pointer = 0;
230 }
231 #endif
232 }
233
os_clock_gettime(void * clock_data,int clock_id,struct timespec * out_struct)234 int os_clock_gettime(void* clock_data, int clock_id, struct timespec* out_struct) {
235 int ret = 0;
236 #if defined(__APPLE__) && defined(__MACH__)
237 wasi_mac_clock_info_t* alloc = (wasi_mac_clock_info_t*) clock_data;
238
239 // From here: https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x/21352348#21352348
240
241 (void)clock_id;
242 // ticks since init
243 uint64_t clock = mach_absolute_time() - alloc->initclock;
244 // nanoseconds since init
245 uint64_t nano = clock * (uint64_t)(alloc->timebase.numer) / (uint64_t)(alloc->timebase.denom);
246 *out_struct = alloc->inittime;
247
248 #define BILLION 1000000000L
249 out_struct->tv_sec += nano / BILLION;
250 out_struct->tv_nsec += nano % BILLION;
251 // normalize
252 out_struct->tv_sec += out_struct->tv_nsec / BILLION;
253 out_struct->tv_nsec = out_struct->tv_nsec % BILLION;
254 #undef BILLION
255 #else
256 ret = clock_gettime(clock_id, out_struct);
257 #endif
258 return ret;
259 }
260
os_clock_getres(void * clock_data,int clock_id,struct timespec * out_struct)261 int os_clock_getres(void* clock_data, int clock_id, struct timespec* out_struct) {
262 int ret = 0;
263 #if defined(__APPLE__) && defined(__MACH__)
264 (void)clock_id;
265 out_struct->tv_sec = 0;
266 out_struct->tv_nsec = 1;
267 #else
268 ret = clock_getres(clock_id, out_struct);
269 #endif
270 return ret;
271 }
272
os_print_last_error(const char * msg)273 void os_print_last_error(const char* msg) {
274 perror(msg);
275 }
276
277 #undef VERBOSE_LOG
278
279 #else
280 // https://stackoverflow.com/questions/26541150/warning-iso-c-forbids-an-empty-translation-unit
281 typedef int make_iso_compilers_happy;
282 #endif