Chromium Code Reviews| Index: runtime/wasm-runtime.cpp |
| diff --git a/runtime/wasm-runtime.cpp b/runtime/wasm-runtime.cpp |
| index d867509ea51d1bf88a579f02d02232f3043b27dc..b90567f7695da3ceb80492e246f636742300f507 100644 |
| --- a/runtime/wasm-runtime.cpp |
| +++ b/runtime/wasm-runtime.cpp |
| @@ -14,8 +14,35 @@ |
| #include <cassert> |
| #include <cmath> |
| +#include <errno.h> |
| +#include <fcntl.h> |
| +#include <iostream> |
| +#include <math.h> |
| // TODO (eholk): change to cstdint |
|
Jim Stichnoth
2016/04/26 22:36:43
You can probably just remove this TODO... It made
Eric Holk
2016/04/27 21:45:20
Done.
|
| #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 |
| namespace { |
| uint32_t HeapBreak; |
| @@ -35,16 +62,46 @@ float floor(float X) { return std::floor(X); } |
| } // end of namespace env |
| // TODO (eholk): move the C parts outside and use C++ name mangling. |
| + |
| +typedef int32_t WasmPtr; |
| + |
| +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; |
| + |
| +long double makeLongDouble(int64_t A, int64_t B) { |
| + union { |
| + int64_t Parts[2]; |
| + long double X; |
| + } Union; |
| + |
| + Union.Parts[0] = A; |
| + Union.Parts[1] = B; |
| + |
| + return Union.X; |
|
Jim Stichnoth
2016/04/26 22:36:43
This kind of type punning is undefined behavior -
Eric Holk
2016/04/27 21:45:20
The functions that call this are meant to provide
|
| +} |
| +} // end of anonymous namespace; |
|
Jim Stichnoth
2016/04/26 22:36:43
no semicolon
Eric Holk
2016/04/27 21:45:20
Done.
|
| + |
| +// TODO (eholk): move the C parts outside and use C++ name mangling. |
| extern "C" { |
| -#include <errno.h> |
| -#include <fcntl.h> |
| -#include <math.h> |
| -#include <stdio.h> |
| -#include <stdlib.h> |
| -#include <string.h> |
| -#include <sys/types.h> |
| -#include <sys/stat.h> |
| -#include <unistd.h> |
| + |
| +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_MEMORY[]; |
| extern uint32_t WASM_DATA_SIZE; |
| @@ -57,11 +114,23 @@ void env$$abort() { |
| void env$$_abort() { env$$abort(); } |
| -double env$$floor_f(float X) { return env::floor(X); } |
| -double env$$floor_d(double X) { return env::floor(X); } |
| +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) { exit(Status); } |
| -void env$$_exit(int Status) { env$$exit(Status); } |
| +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() { \ |
| @@ -70,8 +139,10 @@ void env$$_exit(int Status) { env$$exit(Status); } |
| } |
| int32_t env$$sbrk(int32_t Increment) { |
| + TRACE_ENTRY(); |
| + uint32_t OldBreak = HeapBreak; |
| HeapBreak += Increment; |
| - return HeapBreak; |
| + return trace(OldBreak); |
| } |
| UNIMPLEMENTED(setjmp) |
| @@ -99,82 +170,241 @@ UNIMPLEMENTED(__subtf3) |
| UNIMPLEMENTED(__divtf3) |
| UNIMPLEMENTED(__multf3) |
| UNIMPLEMENTED(__multi3) |
| -UNIMPLEMENTED(__lock) |
| -UNIMPLEMENTED(__unlock) |
| -UNIMPLEMENTED(__syscall6) // sys_close |
| UNIMPLEMENTED(__syscall140) // sys_llseek |
| +UNIMPLEMENTED(__syscall221) // sys_fcntl64 |
| UNIMPLEMENTED(__unordtf2) |
| UNIMPLEMENTED(__fixunstfsi) |
| UNIMPLEMENTED(__floatunsitf) |
| -UNIMPLEMENTED(__extenddftf2) |
| -void *wasmPtr(uint32_t Index) { |
| +void *wasmPtr(int Index) { |
| if (pageNum(Index) < WASM_NUM_PAGES) { |
| return WASM_MEMORY + Index; |
| } |
| abort(); |
| } |
| -extern int __szwasm_main(int, const char **); |
| +WasmPtr toWasm(void *Ptr) { |
| + return reinterpret_cast<WasmPtr>(reinterpret_cast<char *>(Ptr) - WASM_MEMORY); |
| +} |
| + |
| +extern int __szwasm_main(int, WasmPtr); |
| #define WASM_REF(Type, Index) ((Type *)wasmPtr(Index)) |
| #define WASM_DEREF(Type, Index) (*WASM_REF(Type, Index)) |
| int main(int argc, const char **argv) { |
| + // fprintf(stderr, "main(%d)\n", argc); |
| + |
| + // 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 WasmArgV = HeapBreak; |
| + WasmPtr *WasmArgVPtr = WASM_REF(WasmPtr, WasmArgV); |
| + HeapBreak += argc * sizeof(WasmPtr); |
| + |
| + 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, argv); |
| + return __szwasm_main(argc, WasmArgV); |
| } |
| -int env$$abs(int a) { return abs(a); } |
| +int env$$abs(int a) { |
| + TRACE_ENTRY(); |
| + return trace(abs(a)); |
| +} |
| + |
| +clock_t env$$clock() { |
| + TRACE_ENTRY(); |
| + return trace(clock()); |
| +} |
| -double env$$pow(double x, double y) { return pow(x, y); } |
| +WasmPtr env$$ctime(WasmPtr Time) { |
| + TRACE_ENTRY(); |
| + time_t *TimePtr = WASM_REF(time_t, Time); |
| + char *CTime = ctime(TimePtr); |
| + strncpy(GlobalData->StrBuf, CTime, sizeof(GlobalData->StrBuf)); |
| + GlobalData->StrBuf[sizeof(GlobalData->StrBuf) - 1] = '\0'; |
| + return trace(toWasm(GlobalData->StrBuf)); |
| +} |
| + |
| +void env$$__extenddftf2(WasmPtr Dest, double X) { |
| + TRACE_ENTRY(); |
| + long double *DestPtr = WASM_REF(long double, Dest); |
| + *DestPtr = X; |
| + trace(); |
| +} |
| + |
| +void env$$__extendsftf2(WasmPtr Dest, float X) { |
| + TRACE_ENTRY(); |
| + long double *DestPtr = WASM_REF(long double, Dest); |
| + *DestPtr = X; |
| + trace(); |
| +} |
| + |
| +int32_t env$$__letf2(int64_t HighA, int64_t LowA, int64_t HighB, int64_t LowB) { |
| + TRACE_ENTRY(); |
| + return trace(makeLongDouble(HighA, LowA) < makeLongDouble(HighB, LowB)); |
| +} |
| + |
| +double env$$pow(double x, double y) { |
| + TRACE_ENTRY(); |
| + return trace(pow(x, y)); |
| +} |
| + |
| +time_t env$$time(WasmPtr Time) { |
| + TRACE_ENTRY(); |
| + time_t *TimePtr = WASM_REF(time_t, Time); |
| + return trace(time(TimePtr)); |
| +} |
| + |
| +double env$$__trunctfdf2(int64_t High, int64_t Low) { |
| + TRACE_ENTRY(); |
| + return trace(makeLongDouble(High, Low)); |
| +} |
| + |
| +float env$$__trunctfsf2(int64_t High, int64_t Low) { |
| + TRACE_ENTRY(); |
| + return trace(makeLongDouble(High, Low)); |
| +} |
| + |
| +// lock and unlock are no-ops in wasm.js, so we mimic that behavior. |
| +void env$$__lock(int32_t _) { |
| + TRACE_ENTRY(); |
| + (void)_; |
| + trace(); |
| +} |
| + |
| +void env$$__unlock(int32_t _) { |
| + TRACE_ENTRY(); |
| + (void)_; |
| + trace(); |
| +} |
| + |
| +/// sys_read |
| +int env$$__syscall3(int Which, int VarArgs) { |
| + TRACE_ENTRY(); |
| + int Fd = WASM_DEREF(int, VarArgs + 0 * sizeof(int)); |
| + int Buffer = WASM_DEREF(int, VarArgs + 1 * sizeof(int)); |
| + int Length = WASM_DEREF(int, VarArgs + 2 * sizeof(int)); |
| + |
| + return trace(read(Fd, WASM_REF(char *, Buffer), Length)); |
| +} |
| /// sys_write |
| int env$$__syscall4(int Which, int VarArgs) { |
| + TRACE_ENTRY(); |
| int Fd = WASM_DEREF(int, VarArgs + 0 * sizeof(int)); |
| int Buffer = WASM_DEREF(int, VarArgs + 1 * sizeof(int)); |
| int Length = WASM_DEREF(int, VarArgs + 2 * sizeof(int)); |
| - return write(Fd, WASM_REF(char *, Buffer), Length); |
| + return trace(write(Fd, WASM_REF(char *, Buffer), Length)); |
| } |
| /// sys_open |
| int env$$__syscall5(int Which, int VarArgs) { |
| + TRACE_ENTRY(); |
| int WasmPath = WASM_DEREF(int, VarArgs); |
|
Jim Stichnoth
2016/04/26 22:36:43
These WASM_DEREFs should be in terms of <x> * size
|
| int Flags = WASM_DEREF(int, VarArgs + 4); |
| int Mode = WASM_DEREF(int, VarArgs + 8); |
| const char *Path = WASM_REF(char, WasmPath); |
| - fprintf(stderr, "sys_open(%s, %d, %d)\n", Path, Flags, Mode); |
| + return trace(open(Path, Flags, Mode)); |
| +} |
| + |
| +/// sys_close |
| +int env$$__syscall6(int Which, int VarArgs) { |
| + TRACE_ENTRY(); |
| + int Fd = WASM_DEREF(int, VarArgs + 0 * sizeof(int)); |
| + |
| + return trace(close(Fd)); |
| +} |
| + |
| +/// sys_unlink |
| +int env$$__syscall10(int Which, int VarArgs) { |
| + TRACE_ENTRY(); |
| + int WasmPath = WASM_DEREF(int, VarArgs); |
|
Jim Stichnoth
2016/04/26 22:36:43
Above, you have "WASM_DEREF(int, VarArgs + 0 * siz
|
| + const char *Path = WASM_REF(char, WasmPath); |
| - return open(Path, Flags, Mode); |
| + return trace(unlink(Path)); |
| } |
| /// sys_getpid |
| int env$$__syscall20(int Which, int VarArgs) { |
| + TRACE_ENTRY(); |
| (void)Which; |
| (void)VarArgs; |
| - return getpid(); |
| + return trace(getpid()); |
| +} |
| + |
| +/// sys_rmdir |
| +int env$$__syscall40(int Which, int VarArgs) { |
| + TRACE_ENTRY(); |
| + int WasmPath = WASM_DEREF(int, VarArgs); |
| + const char *Path = WASM_REF(char, WasmPath); |
| + |
| + return trace(rmdir(Path)); |
| } |
| /// sys_ioctl |
| int env$$__syscall54(int A, int B) { |
| + TRACE_ENTRY(); |
| int Fd = WASM_DEREF(int, B + 0 * sizeof(int)); |
| int Op = WASM_DEREF(int, B + 1 * sizeof(int)); |
| int ArgP = WASM_DEREF(int, B + 2 * sizeof(int)); |
| - // TODO (eholk): implement sys_ioctl |
| - return -ENOTTY; |
| + |
| + 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); |
| + } |
| } |
| -/// sys_write |
| -int env$$__syscall146(int Which, int VarArgs) { |
| +/// sys_readv |
| +int env$$__syscall145(int Which, int VarArgs) { |
| + TRACE_ENTRY(); |
| + int Fd = WASM_DEREF(int, VarArgs); |
| + int Iov = WASM_DEREF(int, VarArgs + sizeof(int)); |
| + int Iovcnt = WASM_DEREF(int, VarArgs + 2 * sizeof(int)); |
| + int Count = 0; |
| + |
| + for (int I = 0; I < Iovcnt; ++I) { |
| + void *Ptr = WASM_REF(void, WASM_DEREF(int, Iov + I * 8)); |
|
Jim Stichnoth
2016/04/26 22:36:43
Can this be done without the magic 4/8 constants?
|
| + int Length = WASM_DEREF(int, Iov + I * 8 + 4); |
| + |
| + int Curr = read(Fd, Ptr, Length); |
| + |
| + if (Curr < 0) { |
| + return trace(-1); |
| + } |
| + Count += Curr; |
| + } |
| + return trace(Count); |
| +} |
| + |
| +/// sys_writev |
| +int env$$__syscall146(int Which, int VarArgs) { |
| + TRACE_ENTRY(); |
| int Fd = WASM_DEREF(int, VarArgs); |
|
Jim Stichnoth
2016/04/26 22:36:43
VarArgs + <n> * sizeof(int)
Given all this repeti
Eric Holk
2016/04/27 21:45:20
Actually, I think this is better done using C++. W
|
| int Iov = WASM_DEREF(int, VarArgs + sizeof(int)); |
| int Iovcnt = WASM_DEREF(int, VarArgs + 2 * sizeof(int)); |
| @@ -188,20 +418,21 @@ int env$$__syscall146(int Which, int VarArgs) { |
| int Curr = write(Fd, Ptr, Length); |
| if (Curr < 0) { |
| - return -1; |
| + return trace(-1); |
| } |
| Count += Curr; |
| } |
| - return Count; |
| + return trace(Count); |
| } |
| /// sys_mmap_pgoff |
| int env$$__syscall192(int Which, int VarArgs) { |
| + TRACE_ENTRY(); |
| (void)Which; |
| (void)VarArgs; |
| // TODO (eholk): figure out how to implement this. |
| - return -ENOMEM; |
| + return trace(-ENOMEM); |
| } |
| } // end of extern "C" |