• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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