Home | History | Annotate | Download | only in runtime
      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__ << "(...) = "; }
     38 template <typename T> T trace(T x) {
     39   std::cerr << x << std::endl;
     40   return x;
     41 }
     42 void trace() { std::cerr << "(void)" << std::endl; }
     43 #else
     44 #define TRACE_ENTRY()
     45 template <typename T> T trace(T x) { return x; }
     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 
     63 uint32_t pageNum(uint32_t Index) { return Index >> PageSizeLog2; }
     64 } // end of anonymous namespace
     65 
     66 namespace env {
     67 double floor(double X) { return std::floor(X); }
     68 
     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 
     86 int toWasm(void *Ptr) {
     87   return reinterpret_cast<int>(reinterpret_cast<char *>(Ptr) - WASM_MEMORY);
     88 }
     89 
     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:
    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 
    106   WasmPtr(T *Ptr) : Ptr(toWasm(Ptr)) {}
    107 
    108   T &operator*() const { return *asPtr(); }
    109 
    110   T *asPtr() const { return wasmPtr<T>(Ptr); }
    111 
    112   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:
    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 
    126   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 
    133 void __Sz_bounds_fail() {
    134   std::cerr << "Bounds check failure" << std::endl;
    135   abort();
    136 }
    137 
    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 
    145 void env$$abort() {
    146   fprintf(stderr, "Aborting...\n");
    147   abort();
    148 }
    149 
    150 void env$$_abort() { env$$abort(); }
    151 
    152 double env$$floor_f(float X) {
    153   TRACE_ENTRY();
    154   return env::floor(X);
    155 }
    156 double env$$floor_d(double X) {
    157   TRACE_ENTRY();
    158   return env::floor(X);
    159 }
    160 
    161 void env$$exit(int Status) {
    162   TRACE_ENTRY();
    163   exit(Status);
    164 }
    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 
    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 
    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 
    257 int env$$abs(int a) {
    258   TRACE_ENTRY();
    259   return trace(abs(a));
    260 }
    261 
    262 clock_t env$$clock() {
    263   TRACE_ENTRY();
    264   return trace(clock());
    265 }
    266 
    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 
    275 double env$$pow(double x, double y) {
    276   TRACE_ENTRY();
    277   return trace(pow(x, y));
    278 }
    279 
    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.
    287 void env$$__lock(int32_t) {
    288   TRACE_ENTRY();
    289   trace();
    290 }
    291 
    292 void env$$__unlock(int32_t) {
    293   TRACE_ENTRY();
    294   trace();
    295 }
    296 
    297 /// sys_read
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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