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(&micro, 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