|  | //===- subzero/runtime/wasm-runtime.cpp - Subzero WASM runtime source -----===// | 
|  | // | 
|  | //                        The Subzero Code Generator | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This file implements the system calls required by the libc that is included | 
|  | // in WebAssembly programs. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cassert> | 
|  | #include <cmath> | 
|  | #include <iostream> | 
|  | #include <vector> | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <math.h> | 
|  | #include <stdint.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <sys/ioctl.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/stat.h> | 
|  | #include <termios.h> | 
|  | #include <time.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #ifdef WASM_TRACE_RUNTIME | 
|  | #define TRACE_ENTRY()                                                          \ | 
|  | { std::cerr << __func__ << "(...) = "; } | 
|  | template <typename T> T trace(T x) { | 
|  | std::cerr << x << std::endl; | 
|  | return x; | 
|  | } | 
|  | void trace() { std::cerr << "(void)" << std::endl; } | 
|  | #else | 
|  | #define TRACE_ENTRY() | 
|  | template <typename T> T trace(T x) { return x; } | 
|  | void trace() {} | 
|  | #endif // WASM_TRACE_RUNTIME | 
|  |  | 
|  | extern "C" { | 
|  | char *WASM_MEMORY; | 
|  | extern uint32_t WASM_DATA_SIZE; | 
|  | extern uint32_t WASM_NUM_PAGES; | 
|  | } // end of extern "C" | 
|  |  | 
|  | namespace { | 
|  | uint32_t HeapBreak; | 
|  |  | 
|  | // TODO (eholk): make all of these constexpr. | 
|  | const uint32_t PageSizeLog2 = 16; | 
|  | const uint32_t PageSize = 1 << PageSizeLog2; // 64KB | 
|  | const uint32_t StackPtrLoc = 1024;           // defined by emscripten | 
|  |  | 
|  | uint32_t pageNum(uint32_t Index) { return Index >> PageSizeLog2; } | 
|  | } // end of anonymous namespace | 
|  |  | 
|  | namespace env { | 
|  | double floor(double X) { return std::floor(X); } | 
|  |  | 
|  | float floor(float X) { return std::floor(X); } | 
|  | } // end of namespace env | 
|  |  | 
|  | // TODO (eholk): move the C parts outside and use C++ name mangling. | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | /// Some runtime functions need to return pointers. The WasmData struct is used | 
|  | /// to preallocate space for these on the heap. | 
|  | struct WasmData { | 
|  |  | 
|  | /// StrBuf is returned by functions that return strings. | 
|  | char StrBuf[256]; | 
|  | }; | 
|  |  | 
|  | WasmData *GlobalData = NULL; | 
|  |  | 
|  | int toWasm(void *Ptr) { | 
|  | return reinterpret_cast<int>(reinterpret_cast<char *>(Ptr) - WASM_MEMORY); | 
|  | } | 
|  |  | 
|  | template <typename T> T *wasmPtr(int Index) { | 
|  | if (pageNum(Index) < WASM_NUM_PAGES) { | 
|  | return reinterpret_cast<T *>(WASM_MEMORY + Index); | 
|  | } | 
|  | abort(); | 
|  | } | 
|  |  | 
|  | template <typename T> class WasmPtr { | 
|  | int Ptr; | 
|  |  | 
|  | public: | 
|  | WasmPtr(int Ptr) : Ptr(Ptr) { | 
|  | // TODO (eholk): make this a static_assert once we have C++11 | 
|  | assert(sizeof(*this) == sizeof(int)); | 
|  | } | 
|  |  | 
|  | WasmPtr(T *Ptr) : Ptr(toWasm(Ptr)) {} | 
|  |  | 
|  | T &operator*() const { return *asPtr(); } | 
|  |  | 
|  | T *asPtr() const { return wasmPtr<T>(Ptr); } | 
|  |  | 
|  | int asInt() const { return Ptr; } | 
|  | }; | 
|  |  | 
|  | typedef WasmPtr<char> WasmCharPtr; | 
|  |  | 
|  | template <typename T> class WasmArray { | 
|  | int Ptr; | 
|  |  | 
|  | public: | 
|  | WasmArray(int Ptr) : Ptr(Ptr) { | 
|  | // TODO (eholk): make this a static_assert once we have C++11. | 
|  | assert(sizeof(*this) == sizeof(int)); | 
|  | } | 
|  |  | 
|  | T &operator[](unsigned int Index) const { return wasmPtr<T>(Ptr)[Index]; } | 
|  | }; | 
|  | } // end of anonymous namespace | 
|  |  | 
|  | // TODO (eholk): move the C parts outside and use C++ name mangling. | 
|  | extern "C" { | 
|  |  | 
|  | void __Sz_bounds_fail() { | 
|  | std::cerr << "Bounds check failure" << std::endl; | 
|  | abort(); | 
|  | } | 
|  |  | 
|  | void __Sz_indirect_fail() { | 
|  | std::cerr << "Invalid indirect call target" << std::endl; | 
|  | abort(); | 
|  | } | 
|  |  | 
|  | extern char WASM_DATA_INIT[]; | 
|  |  | 
|  | void env$$abort() { | 
|  | fprintf(stderr, "Aborting...\n"); | 
|  | abort(); | 
|  | } | 
|  |  | 
|  | void env$$_abort() { env$$abort(); } | 
|  |  | 
|  | double env$$floor_f(float X) { | 
|  | TRACE_ENTRY(); | 
|  | return env::floor(X); | 
|  | } | 
|  | double env$$floor_d(double X) { | 
|  | TRACE_ENTRY(); | 
|  | return env::floor(X); | 
|  | } | 
|  |  | 
|  | void env$$exit(int Status) { | 
|  | TRACE_ENTRY(); | 
|  | exit(Status); | 
|  | } | 
|  | void env$$_exit(int Status) { | 
|  | TRACE_ENTRY(); | 
|  | env$$exit(Status); | 
|  | } | 
|  |  | 
|  | #define UNIMPLEMENTED(f)                                                       \ | 
|  | void env$$##f() {                                                            \ | 
|  | fprintf(stderr, "Unimplemented: " #f "\n");                                \ | 
|  | abort();                                                                   \ | 
|  | } | 
|  |  | 
|  | int32_t env$$sbrk(int32_t Increment) { | 
|  | TRACE_ENTRY(); | 
|  | uint32_t OldBreak = HeapBreak; | 
|  | HeapBreak += Increment; | 
|  | return trace(OldBreak); | 
|  | } | 
|  |  | 
|  | UNIMPLEMENTED(__addtf3) | 
|  | UNIMPLEMENTED(__assert_fail) | 
|  | UNIMPLEMENTED(__builtin_apply) | 
|  | UNIMPLEMENTED(__builtin_apply_args) | 
|  | UNIMPLEMENTED(__builtin_isinff) | 
|  | UNIMPLEMENTED(__builtin_isinfl) | 
|  | UNIMPLEMENTED(__builtin_malloc) | 
|  | UNIMPLEMENTED(__divtf3) | 
|  | UNIMPLEMENTED(__eqtf2) | 
|  | UNIMPLEMENTED(__extenddftf2) | 
|  | UNIMPLEMENTED(__extendsftf2) | 
|  | UNIMPLEMENTED(__fixsfti) | 
|  | UNIMPLEMENTED(__fixtfdi) | 
|  | UNIMPLEMENTED(__fixtfsi) | 
|  | UNIMPLEMENTED(__fixunstfsi) | 
|  | UNIMPLEMENTED(__floatditf) | 
|  | UNIMPLEMENTED(__floatsitf) | 
|  | UNIMPLEMENTED(__floatunsitf) | 
|  | UNIMPLEMENTED(__getf2) | 
|  | UNIMPLEMENTED(__letf2) | 
|  | UNIMPLEMENTED(__lttf2) | 
|  | UNIMPLEMENTED(__multf3) | 
|  | UNIMPLEMENTED(__multi3) | 
|  | UNIMPLEMENTED(__netf2) | 
|  | UNIMPLEMENTED(__subtf3) | 
|  | UNIMPLEMENTED(__syscall140) // sys_llseek | 
|  | UNIMPLEMENTED(__syscall221) // sys_fcntl64 | 
|  | UNIMPLEMENTED(__trunctfdf2) | 
|  | UNIMPLEMENTED(__trunctfsf2) | 
|  | UNIMPLEMENTED(__unordtf2) | 
|  | UNIMPLEMENTED(longjmp) | 
|  | UNIMPLEMENTED(pthread_cleanup_pop) | 
|  | UNIMPLEMENTED(pthread_cleanup_push) | 
|  | UNIMPLEMENTED(pthread_self) | 
|  | UNIMPLEMENTED(setjmp) | 
|  |  | 
|  | extern int __szwasm_main(int, WasmPtr<WasmCharPtr>); | 
|  |  | 
|  | #define WASM_REF(Type, Index) (WasmPtr<Type>(Index).asPtr()) | 
|  | #define WASM_DEREF(Type, Index) (*WASM_REF(Type, Index)) | 
|  |  | 
|  | int main(int argc, const char **argv) { | 
|  | // Create the heap. | 
|  | std::vector<char> WasmHeap(WASM_NUM_PAGES << PageSizeLog2); | 
|  | WASM_MEMORY = WasmHeap.data(); | 
|  | std::copy(WASM_DATA_INIT, WASM_DATA_INIT + WASM_DATA_SIZE, WasmHeap.begin()); | 
|  |  | 
|  | // TODO (eholk): align these allocations correctly. | 
|  |  | 
|  | // Allocate space for the global data. | 
|  | HeapBreak = WASM_DATA_SIZE; | 
|  | GlobalData = WASM_REF(WasmData, HeapBreak); | 
|  | HeapBreak += sizeof(WasmData); | 
|  |  | 
|  | // copy the command line arguments. | 
|  | WasmPtr<WasmCharPtr> WasmArgV = HeapBreak; | 
|  | WasmPtr<char> *WasmArgVPtr = WasmArgV.asPtr(); | 
|  | HeapBreak += argc * sizeof(*WasmArgVPtr); | 
|  |  | 
|  | for (int i = 0; i < argc; ++i) { | 
|  | WasmArgVPtr[i] = HeapBreak; | 
|  | strcpy(WASM_REF(char, HeapBreak), argv[i]); | 
|  | HeapBreak += strlen(argv[i]) + 1; | 
|  | } | 
|  |  | 
|  | // Initialize the break to the nearest page boundary after the data segment | 
|  | HeapBreak = (WASM_DATA_SIZE + PageSize - 1) & ~(PageSize - 1); | 
|  |  | 
|  | // Initialize the stack pointer. | 
|  | WASM_DEREF(int32_t, StackPtrLoc) = WASM_NUM_PAGES << PageSizeLog2; | 
|  |  | 
|  | return __szwasm_main(argc, WasmArgV); | 
|  | } | 
|  |  | 
|  | int env$$abs(int a) { | 
|  | TRACE_ENTRY(); | 
|  | return trace(abs(a)); | 
|  | } | 
|  |  | 
|  | clock_t env$$clock() { | 
|  | TRACE_ENTRY(); | 
|  | return trace(clock()); | 
|  | } | 
|  |  | 
|  | int env$$ctime(WasmPtr<time_t> Time) { | 
|  | TRACE_ENTRY(); | 
|  | char *CTime = ctime(Time.asPtr()); | 
|  | strncpy(GlobalData->StrBuf, CTime, sizeof(GlobalData->StrBuf)); | 
|  | GlobalData->StrBuf[sizeof(GlobalData->StrBuf) - 1] = '\0'; | 
|  | return trace(WasmPtr<char>(GlobalData->StrBuf).asInt()); | 
|  | } | 
|  |  | 
|  | double env$$pow(double x, double y) { | 
|  | TRACE_ENTRY(); | 
|  | return trace(pow(x, y)); | 
|  | } | 
|  |  | 
|  | time_t env$$time(WasmPtr<time_t> Time) { | 
|  | TRACE_ENTRY(); | 
|  | time_t *TimePtr = WASM_REF(time_t, Time); | 
|  | return trace(time(TimePtr)); | 
|  | } | 
|  |  | 
|  | // lock and unlock are no-ops in wasm.js, so we mimic that behavior. | 
|  | void env$$__lock(int32_t) { | 
|  | TRACE_ENTRY(); | 
|  | trace(); | 
|  | } | 
|  |  | 
|  | void env$$__unlock(int32_t) { | 
|  | TRACE_ENTRY(); | 
|  | trace(); | 
|  | } | 
|  |  | 
|  | /// sys_read | 
|  | int env$$__syscall3(int Which, WasmArray<int> VarArgs) { | 
|  | TRACE_ENTRY(); | 
|  | int Fd = VarArgs[0]; | 
|  | int Buffer = VarArgs[1]; | 
|  | int Length = VarArgs[2]; | 
|  |  | 
|  | return trace(read(Fd, WASM_REF(char *, Buffer), Length)); | 
|  | } | 
|  |  | 
|  | /// sys_write | 
|  | int env$$__syscall4(int Which, WasmArray<int> VarArgs) { | 
|  | TRACE_ENTRY(); | 
|  | int Fd = VarArgs[0]; | 
|  | int Buffer = VarArgs[1]; | 
|  | int Length = VarArgs[2]; | 
|  |  | 
|  | return trace(write(Fd, WASM_REF(char *, Buffer), Length)); | 
|  | } | 
|  |  | 
|  | /// sys_open | 
|  | int env$$__syscall5(int Which, WasmArray<int> VarArgs) { | 
|  | TRACE_ENTRY(); | 
|  | int WasmPath = VarArgs[0]; | 
|  | int Flags = VarArgs[1]; | 
|  | int Mode = VarArgs[2]; | 
|  | const char *Path = WASM_REF(char, WasmPath); | 
|  |  | 
|  | return trace(open(Path, Flags, Mode)); | 
|  | } | 
|  |  | 
|  | /// sys_close | 
|  | int env$$__syscall6(int Which, WasmArray<int> VarArgs) { | 
|  | TRACE_ENTRY(); | 
|  | int Fd = VarArgs[0]; | 
|  |  | 
|  | return trace(close(Fd)); | 
|  | } | 
|  |  | 
|  | /// sys_unlink | 
|  | int env$$__syscall10(int Which, WasmArray<int> VarArgs) { | 
|  | TRACE_ENTRY(); | 
|  | int WasmPath = VarArgs[0]; | 
|  | const char *Path = WASM_REF(char, WasmPath); | 
|  |  | 
|  | return trace(unlink(Path)); | 
|  | } | 
|  |  | 
|  | /// sys_getpid | 
|  | int env$$__syscall20(int Which, WasmArray<int> VarArgs) { | 
|  | TRACE_ENTRY(); | 
|  | (void)Which; | 
|  | (void)VarArgs; | 
|  |  | 
|  | return trace(getpid()); | 
|  | } | 
|  |  | 
|  | /// sys_rmdir | 
|  | int env$$__syscall40(int Which, WasmArray<int> VarArgs) { | 
|  | TRACE_ENTRY(); | 
|  | int WasmPath = VarArgs[0]; | 
|  | const char *Path = WASM_REF(char, WasmPath); | 
|  |  | 
|  | return trace(rmdir(Path)); | 
|  | } | 
|  |  | 
|  | /// sys_ioctl | 
|  | int env$$__syscall54(int Which, WasmArray<int> VarArgs) { | 
|  | TRACE_ENTRY(); | 
|  | int Fd = VarArgs[0]; | 
|  | int Op = VarArgs[1]; | 
|  | int ArgP = VarArgs[2]; | 
|  |  | 
|  | switch (Op) { | 
|  | case TCGETS: { | 
|  | // struct termios has no pointers. Otherwise, we'd have to rewrite them. | 
|  | struct termios *TermIOS = WASM_REF(struct termios, ArgP); | 
|  | return trace(ioctl(Fd, TCGETS, TermIOS)); | 
|  | } | 
|  | default: | 
|  | // TODO (eholk): implement more ioctls | 
|  | return trace(-ENOTTY); | 
|  | } | 
|  | } | 
|  |  | 
|  | struct IoVec { | 
|  | WasmPtr<char> Ptr; | 
|  | int Length; | 
|  | }; | 
|  |  | 
|  | /// sys_readv | 
|  | int env$$__syscall145(int Which, WasmArray<int> VarArgs) { | 
|  | TRACE_ENTRY(); | 
|  | int Fd = VarArgs[0]; | 
|  | WasmArray<IoVec> Iov = VarArgs[1]; | 
|  | int Iovcnt = VarArgs[2]; | 
|  |  | 
|  | int Count = 0; | 
|  |  | 
|  | for (int I = 0; I < Iovcnt; ++I) { | 
|  | int Curr = read(Fd, Iov[I].Ptr.asPtr(), Iov[I].Length); | 
|  |  | 
|  | if (Curr < 0) { | 
|  | return trace(-1); | 
|  | } | 
|  | Count += Curr; | 
|  | } | 
|  | return trace(Count); | 
|  | } | 
|  |  | 
|  | /// sys_writev | 
|  | int env$$__syscall146(int Which, WasmArray<int> VarArgs) { | 
|  | TRACE_ENTRY(); | 
|  | int Fd = VarArgs[0]; | 
|  | WasmArray<IoVec> Iov = VarArgs[1]; | 
|  | int Iovcnt = VarArgs[2]; | 
|  |  | 
|  | int Count = 0; | 
|  |  | 
|  | for (int I = 0; I < Iovcnt; ++I) { | 
|  | int Curr = write(Fd, Iov[I].Ptr.asPtr(), Iov[I].Length); | 
|  |  | 
|  | if (Curr < 0) { | 
|  | return trace(-1); | 
|  | } | 
|  | Count += Curr; | 
|  | } | 
|  | return trace(Count); | 
|  | } | 
|  |  | 
|  | /// sys_mmap_pgoff | 
|  | int env$$__syscall192(int Which, WasmArray<int> VarArgs) { | 
|  | TRACE_ENTRY(); | 
|  | (void)Which; | 
|  | (void)VarArgs; | 
|  |  | 
|  | // TODO (eholk): figure out how to implement this. | 
|  |  | 
|  | return trace(-ENOMEM); | 
|  | } | 
|  | } // end of extern "C" |