1 //===- subzero/runtime/wasm-runtime.cpp - Subzero WASM runtime source -----===//
2 //
3 // The Subzero Code Generator
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file implements the system calls required by the libc that is included
11 // in WebAssembly programs.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include <algorithm>
16 #include <cassert>
17 #include <cmath>
18 #include <iostream>
19 #include <vector>
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <math.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <termios.h>
32 #include <time.h>
33 #include <unistd.h>
34
35 #ifdef WASM_TRACE_RUNTIME
36 #define TRACE_ENTRY() \
37 { std::cerr << __func__ << "(...) = "; }
trace(T x)38 template <typename T> T trace(T x) {
39 std::cerr << x << std::endl;
40 return x;
41 }
trace()42 void trace() { std::cerr << "(void)" << std::endl; }
43 #else
44 #define TRACE_ENTRY()
trace(T x)45 template <typename T> T trace(T x) { return x; }
trace()46 void trace() {}
47 #endif // WASM_TRACE_RUNTIME
48
49 extern "C" {
50 char *WASM_MEMORY;
51 extern uint32_t WASM_DATA_SIZE;
52 extern uint32_t WASM_NUM_PAGES;
53 } // end of extern "C"
54
55 namespace {
56 uint32_t HeapBreak;
57
58 // TODO (eholk): make all of these constexpr.
59 const uint32_t PageSizeLog2 = 16;
60 const uint32_t PageSize = 1 << PageSizeLog2; // 64KB
61 const uint32_t StackPtrLoc = 1024; // defined by emscripten
62
pageNum(uint32_t Index)63 uint32_t pageNum(uint32_t Index) { return Index >> PageSizeLog2; }
64 } // end of anonymous namespace
65
66 namespace env {
floor(double X)67 double floor(double X) { return std::floor(X); }
68
floor(float X)69 float floor(float X) { return std::floor(X); }
70 } // end of namespace env
71
72 // TODO (eholk): move the C parts outside and use C++ name mangling.
73
74 namespace {
75
76 /// Some runtime functions need to return pointers. The WasmData struct is used
77 /// to preallocate space for these on the heap.
78 struct WasmData {
79
80 /// StrBuf is returned by functions that return strings.
81 char StrBuf[256];
82 };
83
84 WasmData *GlobalData = NULL;
85
toWasm(void * Ptr)86 int toWasm(void *Ptr) {
87 return reinterpret_cast<int>(reinterpret_cast<char *>(Ptr) - WASM_MEMORY);
88 }
89
wasmPtr(int Index)90 template <typename T> T *wasmPtr(int Index) {
91 if (pageNum(Index) < WASM_NUM_PAGES) {
92 return reinterpret_cast<T *>(WASM_MEMORY + Index);
93 }
94 abort();
95 }
96
97 template <typename T> class WasmPtr {
98 int Ptr;
99
100 public:
WasmPtr(int Ptr)101 WasmPtr(int Ptr) : Ptr(Ptr) {
102 // TODO (eholk): make this a static_assert once we have C++11
103 assert(sizeof(*this) == sizeof(int));
104 }
105
WasmPtr(T * Ptr)106 WasmPtr(T *Ptr) : Ptr(toWasm(Ptr)) {}
107
operator *() const108 T &operator*() const { return *asPtr(); }
109
asPtr() const110 T *asPtr() const { return wasmPtr<T>(Ptr); }
111
asInt() const112 int asInt() const { return Ptr; }
113 };
114
115 typedef WasmPtr<char> WasmCharPtr;
116
117 template <typename T> class WasmArray {
118 int Ptr;
119
120 public:
WasmArray(int Ptr)121 WasmArray(int Ptr) : Ptr(Ptr) {
122 // TODO (eholk): make this a static_assert once we have C++11.
123 assert(sizeof(*this) == sizeof(int));
124 }
125
operator [](unsigned int Index) const126 T &operator[](unsigned int Index) const { return wasmPtr<T>(Ptr)[Index]; }
127 };
128 } // end of anonymous namespace
129
130 // TODO (eholk): move the C parts outside and use C++ name mangling.
131 extern "C" {
132
__Sz_bounds_fail()133 void __Sz_bounds_fail() {
134 std::cerr << "Bounds check failure" << std::endl;
135 abort();
136 }
137
__Sz_indirect_fail()138 void __Sz_indirect_fail() {
139 std::cerr << "Invalid indirect call target" << std::endl;
140 abort();
141 }
142
143 extern char WASM_DATA_INIT[];
144
env$$abort()145 void env$$abort() {
146 fprintf(stderr, "Aborting...\n");
147 abort();
148 }
149
env$$_abort()150 void env$$_abort() { env$$abort(); }
151
env$$floor_f(float X)152 double env$$floor_f(float X) {
153 TRACE_ENTRY();
154 return env::floor(X);
155 }
env$$floor_d(double X)156 double env$$floor_d(double X) {
157 TRACE_ENTRY();
158 return env::floor(X);
159 }
160
env$$exit(int Status)161 void env$$exit(int Status) {
162 TRACE_ENTRY();
163 exit(Status);
164 }
env$$_exit(int Status)165 void env$$_exit(int Status) {
166 TRACE_ENTRY();
167 env$$exit(Status);
168 }
169
170 #define UNIMPLEMENTED(f) \
171 void env$$##f() { \
172 fprintf(stderr, "Unimplemented: " #f "\n"); \
173 abort(); \
174 }
175
env$$sbrk(int32_t Increment)176 int32_t env$$sbrk(int32_t Increment) {
177 TRACE_ENTRY();
178 uint32_t OldBreak = HeapBreak;
179 HeapBreak += Increment;
180 return trace(OldBreak);
181 }
182
183 UNIMPLEMENTED(__addtf3)
184 UNIMPLEMENTED(__assert_fail)
185 UNIMPLEMENTED(__builtin_apply)
186 UNIMPLEMENTED(__builtin_apply_args)
187 UNIMPLEMENTED(__builtin_isinff)
188 UNIMPLEMENTED(__builtin_isinfl)
189 UNIMPLEMENTED(__builtin_malloc)
190 UNIMPLEMENTED(__divtf3)
191 UNIMPLEMENTED(__eqtf2)
192 UNIMPLEMENTED(__extenddftf2)
193 UNIMPLEMENTED(__extendsftf2)
194 UNIMPLEMENTED(__fixsfti)
195 UNIMPLEMENTED(__fixtfdi)
196 UNIMPLEMENTED(__fixtfsi)
197 UNIMPLEMENTED(__fixunstfsi)
198 UNIMPLEMENTED(__floatditf)
199 UNIMPLEMENTED(__floatsitf)
200 UNIMPLEMENTED(__floatunsitf)
201 UNIMPLEMENTED(__getf2)
202 UNIMPLEMENTED(__letf2)
203 UNIMPLEMENTED(__lttf2)
204 UNIMPLEMENTED(__multf3)
205 UNIMPLEMENTED(__multi3)
206 UNIMPLEMENTED(__netf2)
207 UNIMPLEMENTED(__subtf3)
208 UNIMPLEMENTED(__syscall140) // sys_llseek
209 UNIMPLEMENTED(__syscall221) // sys_fcntl64
210 UNIMPLEMENTED(__trunctfdf2)
211 UNIMPLEMENTED(__trunctfsf2)
212 UNIMPLEMENTED(__unordtf2)
213 UNIMPLEMENTED(longjmp)
214 UNIMPLEMENTED(pthread_cleanup_pop)
215 UNIMPLEMENTED(pthread_cleanup_push)
216 UNIMPLEMENTED(pthread_self)
217 UNIMPLEMENTED(setjmp)
218
219 extern int __szwasm_main(int, WasmPtr<WasmCharPtr>);
220
221 #define WASM_REF(Type, Index) (WasmPtr<Type>(Index).asPtr())
222 #define WASM_DEREF(Type, Index) (*WASM_REF(Type, Index))
223
main(int argc,const char ** argv)224 int main(int argc, const char **argv) {
225 // Create the heap.
226 std::vector<char> WasmHeap(WASM_NUM_PAGES << PageSizeLog2);
227 WASM_MEMORY = WasmHeap.data();
228 std::copy(WASM_DATA_INIT, WASM_DATA_INIT + WASM_DATA_SIZE, WasmHeap.begin());
229
230 // TODO (eholk): align these allocations correctly.
231
232 // Allocate space for the global data.
233 HeapBreak = WASM_DATA_SIZE;
234 GlobalData = WASM_REF(WasmData, HeapBreak);
235 HeapBreak += sizeof(WasmData);
236
237 // copy the command line arguments.
238 WasmPtr<WasmCharPtr> WasmArgV = HeapBreak;
239 WasmPtr<char> *WasmArgVPtr = WasmArgV.asPtr();
240 HeapBreak += argc * sizeof(*WasmArgVPtr);
241
242 for (int i = 0; i < argc; ++i) {
243 WasmArgVPtr[i] = HeapBreak;
244 strcpy(WASM_REF(char, HeapBreak), argv[i]);
245 HeapBreak += strlen(argv[i]) + 1;
246 }
247
248 // Initialize the break to the nearest page boundary after the data segment
249 HeapBreak = (WASM_DATA_SIZE + PageSize - 1) & ~(PageSize - 1);
250
251 // Initialize the stack pointer.
252 WASM_DEREF(int32_t, StackPtrLoc) = WASM_NUM_PAGES << PageSizeLog2;
253
254 return __szwasm_main(argc, WasmArgV);
255 }
256
env$$abs(int a)257 int env$$abs(int a) {
258 TRACE_ENTRY();
259 return trace(abs(a));
260 }
261
env$$clock()262 clock_t env$$clock() {
263 TRACE_ENTRY();
264 return trace(clock());
265 }
266
env$$ctime(WasmPtr<time_t> Time)267 int env$$ctime(WasmPtr<time_t> Time) {
268 TRACE_ENTRY();
269 char *CTime = ctime(Time.asPtr());
270 strncpy(GlobalData->StrBuf, CTime, sizeof(GlobalData->StrBuf));
271 GlobalData->StrBuf[sizeof(GlobalData->StrBuf) - 1] = '\0';
272 return trace(WasmPtr<char>(GlobalData->StrBuf).asInt());
273 }
274
env$$pow(double x,double y)275 double env$$pow(double x, double y) {
276 TRACE_ENTRY();
277 return trace(pow(x, y));
278 }
279
env$$time(WasmPtr<time_t> Time)280 time_t env$$time(WasmPtr<time_t> Time) {
281 TRACE_ENTRY();
282 time_t *TimePtr = WASM_REF(time_t, Time);
283 return trace(time(TimePtr));
284 }
285
286 // lock and unlock are no-ops in wasm.js, so we mimic that behavior.
env$$__lock(int32_t)287 void env$$__lock(int32_t) {
288 TRACE_ENTRY();
289 trace();
290 }
291
env$$__unlock(int32_t)292 void env$$__unlock(int32_t) {
293 TRACE_ENTRY();
294 trace();
295 }
296
297 /// sys_read
env$$__syscall3(int Which,WasmArray<int> VarArgs)298 int env$$__syscall3(int Which, WasmArray<int> VarArgs) {
299 TRACE_ENTRY();
300 int Fd = VarArgs[0];
301 int Buffer = VarArgs[1];
302 int Length = VarArgs[2];
303
304 return trace(read(Fd, WASM_REF(char *, Buffer), Length));
305 }
306
307 /// sys_write
env$$__syscall4(int Which,WasmArray<int> VarArgs)308 int env$$__syscall4(int Which, WasmArray<int> VarArgs) {
309 TRACE_ENTRY();
310 int Fd = VarArgs[0];
311 int Buffer = VarArgs[1];
312 int Length = VarArgs[2];
313
314 return trace(write(Fd, WASM_REF(char *, Buffer), Length));
315 }
316
317 /// sys_open
env$$__syscall5(int Which,WasmArray<int> VarArgs)318 int env$$__syscall5(int Which, WasmArray<int> VarArgs) {
319 TRACE_ENTRY();
320 int WasmPath = VarArgs[0];
321 int Flags = VarArgs[1];
322 int Mode = VarArgs[2];
323 const char *Path = WASM_REF(char, WasmPath);
324
325 return trace(open(Path, Flags, Mode));
326 }
327
328 /// sys_close
env$$__syscall6(int Which,WasmArray<int> VarArgs)329 int env$$__syscall6(int Which, WasmArray<int> VarArgs) {
330 TRACE_ENTRY();
331 int Fd = VarArgs[0];
332
333 return trace(close(Fd));
334 }
335
336 /// sys_unlink
env$$__syscall10(int Which,WasmArray<int> VarArgs)337 int env$$__syscall10(int Which, WasmArray<int> VarArgs) {
338 TRACE_ENTRY();
339 int WasmPath = VarArgs[0];
340 const char *Path = WASM_REF(char, WasmPath);
341
342 return trace(unlink(Path));
343 }
344
345 /// sys_getpid
env$$__syscall20(int Which,WasmArray<int> VarArgs)346 int env$$__syscall20(int Which, WasmArray<int> VarArgs) {
347 TRACE_ENTRY();
348 (void)Which;
349 (void)VarArgs;
350
351 return trace(getpid());
352 }
353
354 /// sys_rmdir
env$$__syscall40(int Which,WasmArray<int> VarArgs)355 int env$$__syscall40(int Which, WasmArray<int> VarArgs) {
356 TRACE_ENTRY();
357 int WasmPath = VarArgs[0];
358 const char *Path = WASM_REF(char, WasmPath);
359
360 return trace(rmdir(Path));
361 }
362
363 /// sys_ioctl
env$$__syscall54(int Which,WasmArray<int> VarArgs)364 int env$$__syscall54(int Which, WasmArray<int> VarArgs) {
365 TRACE_ENTRY();
366 int Fd = VarArgs[0];
367 int Op = VarArgs[1];
368 int ArgP = VarArgs[2];
369
370 switch (Op) {
371 case TCGETS: {
372 // struct termios has no pointers. Otherwise, we'd have to rewrite them.
373 struct termios *TermIOS = WASM_REF(struct termios, ArgP);
374 return trace(ioctl(Fd, TCGETS, TermIOS));
375 }
376 default:
377 // TODO (eholk): implement more ioctls
378 return trace(-ENOTTY);
379 }
380 }
381
382 struct IoVec {
383 WasmPtr<char> Ptr;
384 int Length;
385 };
386
387 /// sys_readv
env$$__syscall145(int Which,WasmArray<int> VarArgs)388 int env$$__syscall145(int Which, WasmArray<int> VarArgs) {
389 TRACE_ENTRY();
390 int Fd = VarArgs[0];
391 WasmArray<IoVec> Iov = VarArgs[1];
392 int Iovcnt = VarArgs[2];
393
394 int Count = 0;
395
396 for (int I = 0; I < Iovcnt; ++I) {
397 int Curr = read(Fd, Iov[I].Ptr.asPtr(), Iov[I].Length);
398
399 if (Curr < 0) {
400 return trace(-1);
401 }
402 Count += Curr;
403 }
404 return trace(Count);
405 }
406
407 /// sys_writev
env$$__syscall146(int Which,WasmArray<int> VarArgs)408 int env$$__syscall146(int Which, WasmArray<int> VarArgs) {
409 TRACE_ENTRY();
410 int Fd = VarArgs[0];
411 WasmArray<IoVec> Iov = VarArgs[1];
412 int Iovcnt = VarArgs[2];
413
414 int Count = 0;
415
416 for (int I = 0; I < Iovcnt; ++I) {
417 int Curr = write(Fd, Iov[I].Ptr.asPtr(), Iov[I].Length);
418
419 if (Curr < 0) {
420 return trace(-1);
421 }
422 Count += Curr;
423 }
424 return trace(Count);
425 }
426
427 /// sys_mmap_pgoff
env$$__syscall192(int Which,WasmArray<int> VarArgs)428 int env$$__syscall192(int Which, WasmArray<int> VarArgs) {
429 TRACE_ENTRY();
430 (void)Which;
431 (void)VarArgs;
432
433 // TODO (eholk): figure out how to implement this.
434
435 return trace(-ENOMEM);
436 }
437 } // end of extern "C"
438