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