| /* |
| * Copyright 2020 WebAssembly Community Group participants |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * This is an experiment and currently extremely limited implementation |
| * of the WASI syscall API. The implementation of the API itself is coming from |
| * uvwasi: https://github.com/cjihrig/uvwasi. |
| * |
| * Most of the code in the file is mostly marshelling data between the wabt |
| * interpreter and uvwasi. |
| * |
| * For details of the WASI api see: |
| * https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md |
| * and the C headers version: |
| * https://github.com/WebAssembly/wasi-libc/blob/master/libc-bottom-half/headers/public/wasi/api.h |
| */ |
| |
| #include "wabt/interp/interp-wasi.h" |
| #include "wabt/interp/interp-util.h" |
| |
| #ifdef WITH_WASI |
| |
| #include "uvwasi.h" |
| |
| #include <cinttypes> |
| #include <unordered_map> |
| |
| using namespace wabt; |
| using namespace wabt::interp; |
| |
| namespace { |
| |
| // Types that align with WASI spec on size and alignment. These are |
| // copied directly from wasi-lib's auto-generated api.h |
| // TODO(sbc): Auto-generate this from witx. |
| |
| // BEGIN: wasi.h types from wasi-libc |
| |
| using __wasi_size_t = uint32_t; |
| using __wasi_ptr_t = uint32_t; |
| |
| static_assert(sizeof(__wasi_size_t) == 4, "witx calculated size"); |
| static_assert(alignof(__wasi_size_t) == 4, "witx calculated align"); |
| static_assert(sizeof(__wasi_ptr_t) == 4, "witx calculated size"); |
| static_assert(alignof(__wasi_ptr_t) == 4, "witx calculated align"); |
| |
| struct __wasi_prestat_dir_t { |
| __wasi_size_t pr_name_len; |
| }; |
| |
| using __wasi_preopentype_t = uint8_t; |
| using __wasi_rights_t = uint64_t; |
| using __wasi_fdflags_t = uint16_t; |
| using __wasi_filetype_t = uint8_t; |
| using __wasi_oflags_t = uint16_t; |
| using __wasi_lookupflags_t = uint32_t; |
| using __wasi_fd_t = uint32_t; |
| using __wasi_timestamp_t = uint64_t; |
| using __wasi_whence_t = uint8_t; |
| using __wasi_filedelta_t = int64_t; |
| using __wasi_filesize_t = uint64_t; |
| |
| union __wasi_prestat_u_t { |
| __wasi_prestat_dir_t dir; |
| }; |
| |
| struct __wasi_prestat_t { |
| __wasi_preopentype_t tag; |
| __wasi_prestat_u_t u; |
| }; |
| |
| struct __wasi_fdstat_t { |
| __wasi_filetype_t fs_filetype; |
| __wasi_fdflags_t fs_flags; |
| __wasi_rights_t fs_rights_base; |
| __wasi_rights_t fs_rights_inheriting; |
| }; |
| |
| static_assert(sizeof(__wasi_fdstat_t) == 24, "witx calculated size"); |
| static_assert(alignof(__wasi_fdstat_t) == 8, "witx calculated align"); |
| static_assert(offsetof(__wasi_fdstat_t, fs_filetype) == 0, |
| "witx calculated offset"); |
| static_assert(offsetof(__wasi_fdstat_t, fs_flags) == 2, |
| "witx calculated offset"); |
| static_assert(offsetof(__wasi_fdstat_t, fs_rights_base) == 8, |
| "witx calculated offset"); |
| static_assert(offsetof(__wasi_fdstat_t, fs_rights_inheriting) == 16, |
| "witx calculated offset"); |
| |
| struct __wasi_iovec_t { |
| __wasi_ptr_t buf; |
| __wasi_size_t buf_len; |
| }; |
| |
| static_assert(sizeof(__wasi_iovec_t) == 8, "witx calculated size"); |
| static_assert(alignof(__wasi_iovec_t) == 4, "witx calculated align"); |
| static_assert(offsetof(__wasi_iovec_t, buf) == 0, "witx calculated offset"); |
| static_assert(offsetof(__wasi_iovec_t, buf_len) == 4, "witx calculated offset"); |
| |
| using __wasi_device_t = uint64_t; |
| |
| static_assert(sizeof(__wasi_device_t) == 8, "witx calculated size"); |
| static_assert(alignof(__wasi_device_t) == 8, "witx calculated align"); |
| |
| using __wasi_inode_t = uint64_t; |
| |
| static_assert(sizeof(__wasi_inode_t) == 8, "witx calculated size"); |
| static_assert(alignof(__wasi_inode_t) == 8, "witx calculated align"); |
| |
| using __wasi_linkcount_t = uint64_t; |
| |
| static_assert(sizeof(__wasi_linkcount_t) == 8, "witx calculated size"); |
| static_assert(alignof(__wasi_linkcount_t) == 8, "witx calculated align"); |
| |
| struct __wasi_filestat_t { |
| __wasi_device_t dev; |
| __wasi_inode_t ino; |
| __wasi_filetype_t filetype; |
| __wasi_linkcount_t nlink; |
| __wasi_filesize_t size; |
| __wasi_timestamp_t atim; |
| __wasi_timestamp_t mtim; |
| __wasi_timestamp_t ctim; |
| }; |
| |
| static_assert(sizeof(__wasi_filestat_t) == 64, "witx calculated size"); |
| static_assert(alignof(__wasi_filestat_t) == 8, "witx calculated align"); |
| static_assert(offsetof(__wasi_filestat_t, dev) == 0, "witx calculated offset"); |
| static_assert(offsetof(__wasi_filestat_t, ino) == 8, "witx calculated offset"); |
| static_assert(offsetof(__wasi_filestat_t, filetype) == 16, |
| "witx calculated offset"); |
| static_assert(offsetof(__wasi_filestat_t, nlink) == 24, |
| "witx calculated offset"); |
| static_assert(offsetof(__wasi_filestat_t, size) == 32, |
| "witx calculated offset"); |
| static_assert(offsetof(__wasi_filestat_t, atim) == 40, |
| "witx calculated offset"); |
| static_assert(offsetof(__wasi_filestat_t, mtim) == 48, |
| "witx calculated offset"); |
| static_assert(offsetof(__wasi_filestat_t, ctim) == 56, |
| "witx calculated offset"); |
| |
| #define __WASI_ERRNO_SUCCESS (UINT16_C(0)) |
| #define __WASI_ERRNO_NOENT (UINT16_C(44)) |
| |
| // END wasi.h types from wasi-lib |
| |
| class WasiInstance { |
| public: |
| WasiInstance(Instance::Ptr instance, |
| uvwasi_s* uvwasi, |
| Memory* memory, |
| Stream* trace_stream) |
| : trace_stream(trace_stream), |
| instance(instance), |
| uvwasi(uvwasi), |
| memory(memory) {} |
| |
| Result random_get(const Values& params, Values& results, Trap::Ptr* trap) { |
| /* __wasi_errno_t __wasi_random_get(uint8_t * buf, __wasi_size_t buf_len) */ |
| assert(false); |
| return Result::Ok; |
| } |
| |
| Result proc_exit(const Values& params, Values& results, Trap::Ptr* trap) { |
| const Value arg0 = params[0]; |
| uvwasi_proc_exit(uvwasi, arg0.Get<u32>()); |
| return Result::Ok; |
| } |
| |
| Result poll_oneoff(const Values& params, Values& results, Trap::Ptr* trap) { |
| assert(false); |
| return Result::Ok; |
| } |
| |
| Result clock_time_get(const Values& params, |
| Values& results, |
| Trap::Ptr* trap) { |
| /* __wasi_errno_t __wasi_clock_time_get(__wasi_clockid_t id, |
| * __wasi_timestamp_t precision, |
| * __wasi_timestamp_t *time) |
| */ |
| __wasi_timestamp_t t; |
| results[0].Set<u32>(uvwasi_clock_time_get(uvwasi, params[0].Get<u32>(), |
| params[1].Get<u64>(), &t)); |
| uint32_t time_ptr = params[2].Get<u32>(); |
| CHECK_RESULT(writeValue<__wasi_timestamp_t>(t, time_ptr, trap)); |
| return Result::Ok; |
| } |
| |
| Result path_rename(const Values& params, Values& results, Trap::Ptr* trap) { |
| assert(false); |
| return Result::Ok; |
| } |
| |
| Result path_open(const Values& params, Values& results, Trap::Ptr* trap) { |
| /* __wasi_errno_t __wasi_path_open(__wasi_fd_t fd, |
| __wasi_lookupflags_t dirflags, |
| const char *path, |
| size_t path_len, |
| __wasi_oflags_t oflags, |
| __wasi_rights_t fs_rights_base, |
| __wasi_rights_t fs_rights_inherting, |
| __wasi_fdflags_t fdflags, |
| __wasi_fd_t *opened_fd) */ |
| uvwasi_fd_t dirfd = params[0].Get<u32>(); |
| __wasi_lookupflags_t dirflags = params[1].Get<u32>(); |
| uint32_t path_ptr = params[2].Get<u32>(); |
| __wasi_size_t path_len = params[3].Get<u32>(); |
| __wasi_oflags_t oflags = params[4].Get<u32>(); |
| __wasi_rights_t fs_rights_base = params[5].Get<u32>(); |
| __wasi_rights_t fs_rights_inherting = params[6].Get<u32>(); |
| __wasi_fdflags_t fs_flags = params[7].Get<u32>(); |
| uint32_t out_ptr = params[8].Get<u32>(); |
| char* path; |
| CHECK_RESULT(getMemPtr<char>(path_ptr, path_len, &path, trap)); |
| if (trace_stream) { |
| trace_stream->Writef("path_open : %s\n", path); |
| } |
| uvwasi_fd_t outfd; |
| results[0].Set<u32>(uvwasi_path_open( |
| uvwasi, dirfd, dirflags, path, path_len, oflags, fs_rights_base, |
| fs_rights_inherting, fs_flags, &outfd)); |
| if (trace_stream) { |
| trace_stream->Writef("path_open -> %d\n", results[0].Get<u32>()); |
| } |
| CHECK_RESULT(writeValue<__wasi_fd_t>(outfd, out_ptr, trap)); |
| return Result::Ok; |
| } |
| |
| Result path_filestat_get(const Values& params, |
| Values& results, |
| Trap::Ptr* trap) { |
| /* __wasi_errno_t __wasi_path_filestat_get(__wasi_fd_t fd, |
| * __wasi_lookupflags_t flags, |
| * const char *path, |
| * size_t path_len, |
| * __wasi_filestat_t *buf |
| */ |
| uvwasi_fd_t fd = params[0].Get<u32>(); |
| __wasi_lookupflags_t flags = params[1].Get<u32>(); |
| uint32_t path_ptr = params[2].Get<u32>(); |
| uvwasi_size_t path_len = params[3].Get<u32>(); |
| uint32_t filestat_ptr = params[4].Get<u32>(); |
| char* path; |
| CHECK_RESULT(getMemPtr<char>(path_ptr, path_len, &path, trap)); |
| if (trace_stream) { |
| trace_stream->Writef("path_filestat_get : %d %s\n", fd, path); |
| } |
| uvwasi_filestat_t buf; |
| results[0].Set<u32>( |
| uvwasi_path_filestat_get(uvwasi, fd, flags, path, path_len, &buf)); |
| __wasi_filestat_t* filestat; |
| CHECK_RESULT(getMemPtr<__wasi_filestat_t>( |
| filestat_ptr, sizeof(__wasi_filestat_t), &filestat, trap)); |
| uvwasi_serdes_write_filestat_t(filestat, 0, &buf); |
| if (trace_stream) { |
| trace_stream->Writef("path_filestat_get -> size=%" PRIu64 " %d\n", |
| buf.st_size, results[0].Get<u32>()); |
| } |
| return Result::Ok; |
| } |
| |
| Result path_symlink(const Values& params, Values& results, Trap::Ptr* trap) { |
| /* __wasi_errno_t __wasi_path_symlink(const char *old_path, |
| * size_t old_path_len, |
| * __wasi_fd_t fd, |
| * const char *new_path, |
| * size_t new_path_len); |
| */ |
| |
| uint32_t old_path_ptr = params[0].Get<u32>(); |
| __wasi_size_t old_path_len = params[1].Get<u32>(); |
| uvwasi_fd_t fd = params[2].Get<u32>(); |
| uint32_t new_path_ptr = params[3].Get<u32>(); |
| __wasi_size_t new_path_len = params[4].Get<u32>(); |
| char* old_path; |
| char* new_path; |
| CHECK_RESULT(getMemPtr<char>(old_path_ptr, old_path_len, &old_path, trap)); |
| CHECK_RESULT(getMemPtr<char>(new_path_ptr, new_path_len, &new_path, trap)); |
| if (trace_stream) { |
| trace_stream->Writef("path_symlink %d %s : %s\n", fd, old_path, new_path); |
| } |
| results[0].Set<u32>(uvwasi_path_symlink(uvwasi, old_path, old_path_len, fd, |
| new_path, new_path_len)); |
| if (trace_stream) { |
| trace_stream->Writef("path_symlink -> %d\n", results[0].Get<u32>()); |
| } |
| return Result::Ok; |
| } |
| |
| Result path_readlink(const Values& params, Values& results, Trap::Ptr* trap) { |
| assert(false); |
| return Result::Ok; |
| } |
| |
| Result path_create_directory(const Values& params, |
| Values& results, |
| Trap::Ptr* trap) { |
| assert(false); |
| return Result::Ok; |
| } |
| |
| Result path_remove_directory(const Values& params, |
| Values& results, |
| Trap::Ptr* trap) { |
| assert(false); |
| return Result::Ok; |
| } |
| |
| Result path_unlink_file(const Values& params, |
| Values& results, |
| Trap::Ptr* trap) { |
| /* __wasi_errno_t __wasi_path_unlink_file(__wasi_fd_t fd, |
| * const char *path, |
| * size_t path_len) |
| */ |
| uvwasi_fd_t fd = params[0].Get<u32>(); |
| uint32_t path_ptr = params[1].Get<u32>(); |
| __wasi_size_t path_len = params[2].Get<u32>(); |
| char* path; |
| CHECK_RESULT(getMemPtr<char>(path_ptr, path_len, &path, trap)); |
| if (trace_stream) { |
| trace_stream->Writef("path_unlink_file %d %s\n", fd, path); |
| } |
| results[0].Set<u32>(uvwasi_path_unlink_file(uvwasi, fd, path, path_len)); |
| return Result::Ok; |
| } |
| |
| Result fd_prestat_get(const Values& params, |
| Values& results, |
| Trap::Ptr* trap) { |
| /* __wasi_errno_t __wasi_fd_prestat_get(__wasi_fd_t fd, |
| * __wasi_prestat_t *buf)) |
| */ |
| uvwasi_fd_t fd = params[0].Get<u32>(); |
| uint32_t prestat_ptr = params[1].Get<u32>(); |
| if (trace_stream) { |
| trace_stream->Writef("fd_prestat_get %d\n", fd); |
| } |
| uvwasi_prestat_t buf; |
| results[0].Set<u32>(uvwasi_fd_prestat_get(uvwasi, fd, &buf)); |
| __wasi_prestat_t* prestat; |
| CHECK_RESULT(getMemPtr<__wasi_prestat_t>(prestat_ptr, 1, &prestat, trap)); |
| uvwasi_serdes_write_prestat_t(prestat, 0, &buf); |
| if (trace_stream) { |
| trace_stream->Writef("fd_prestat_get -> %d\n", results[0].Get<u32>()); |
| } |
| return Result::Ok; |
| } |
| |
| Result fd_prestat_dir_name(const Values& params, |
| Values& results, |
| Trap::Ptr* trap) { |
| uvwasi_fd_t fd = params[0].Get<u32>(); |
| uint32_t path_ptr = params[1].Get<u32>(); |
| uvwasi_size_t path_len = params[2].Get<u32>(); |
| if (trace_stream) { |
| trace_stream->Writef("fd_prestat_dir_name %d %d %d\n", fd, path_ptr, |
| path_len); |
| } |
| char* path; |
| CHECK_RESULT(getMemPtr<char>(path_ptr, path_len, &path, trap)); |
| results[0].Set<u32>(uvwasi_fd_prestat_dir_name(uvwasi, fd, path, path_len)); |
| if (trace_stream) { |
| trace_stream->Writef("fd_prestat_dir_name %d -> %d %s %d\n", fd, |
| results[0].Get<u32>(), path, path_len); |
| } |
| return Result::Ok; |
| } |
| |
| Result fd_filestat_get(const Values& params, |
| Values& results, |
| Trap::Ptr* trap) { |
| /* __wasi_fd_filestat_get(__wasi_fd_t f, __wasi_filestat_t *buf) */ |
| uvwasi_fd_t fd = params[0].Get<u32>(); |
| uint32_t filestat_ptr = params[1].Get<u32>(); |
| uvwasi_filestat_t buf; |
| results[0].Set<u32>(uvwasi_fd_filestat_get(uvwasi, fd, &buf)); |
| __wasi_filestat_t* filestat; |
| CHECK_RESULT(getMemPtr<__wasi_filestat_t>( |
| filestat_ptr, sizeof(__wasi_filestat_t), &filestat, trap)); |
| uvwasi_serdes_write_filestat_t(filestat, 0, &buf); |
| if (trace_stream) { |
| trace_stream->Writef("fd_filestat_get -> size=%" PRIu64 " %d\n", |
| buf.st_size, results[0].Get<u32>()); |
| } |
| return Result::Ok; |
| } |
| |
| Result fd_fdstat_set_flags(const Values& params, |
| Values& results, |
| Trap::Ptr* trap) { |
| assert(false); |
| return Result::Ok; |
| } |
| |
| Result fd_fdstat_get(const Values& params, Values& results, Trap::Ptr* trap) { |
| int32_t fd = params[0].Get<u32>(); |
| uint32_t stat_ptr = params[1].Get<u32>(); |
| if (trace_stream) { |
| trace_stream->Writef("fd_fdstat_get %d\n", fd); |
| } |
| CHECK_RESULT(getMemPtr<__wasi_fdstat_t>(stat_ptr, 1, nullptr, trap)); |
| uvwasi_fdstat_t host_statbuf; |
| results[0].Set<u32>(uvwasi_fd_fdstat_get(uvwasi, fd, &host_statbuf)); |
| |
| // Write the host statbuf into the target wasm memory |
| __wasi_fdstat_t* statbuf; |
| CHECK_RESULT(getMemPtr<__wasi_fdstat_t>(stat_ptr, 1, &statbuf, trap)); |
| uvwasi_serdes_write_fdstat_t(statbuf, 0, &host_statbuf); |
| return Result::Ok; |
| } |
| |
| Result fd_read(const Values& params, Values& results, Trap::Ptr* trap) { |
| int32_t fd = params[0].Get<u32>(); |
| int32_t iovptr = params[1].Get<u32>(); |
| int32_t iovcnt = params[2].Get<u32>(); |
| int32_t out_ptr = params[3].Get<u32>(); |
| if (trace_stream) { |
| trace_stream->Writef("fd_read %d [%d]\n", fd, iovcnt); |
| } |
| __wasi_iovec_t* wasm_iovs; |
| CHECK_RESULT(getMemPtr<__wasi_iovec_t>(iovptr, iovcnt, &wasm_iovs, trap)); |
| std::vector<uvwasi_iovec_t> iovs(iovcnt); |
| for (int i = 0; i < iovcnt; i++) { |
| iovs[i].buf_len = wasm_iovs[i].buf_len; |
| |
| CHECK_RESULT(getMemPtr<uint8_t>(wasm_iovs[i].buf, wasm_iovs[i].buf_len, |
| reinterpret_cast<uint8_t**>(&iovs[i].buf), |
| trap)); |
| } |
| __wasi_ptr_t* out_addr; |
| CHECK_RESULT(getMemPtr<__wasi_ptr_t>(out_ptr, 1, &out_addr, trap)); |
| results[0].Set<u32>( |
| uvwasi_fd_read(uvwasi, fd, iovs.data(), iovs.size(), out_addr)); |
| if (trace_stream) { |
| trace_stream->Writef("fd_read -> %d\n", results[0].Get<u32>()); |
| } |
| return Result::Ok; |
| } |
| |
| Result fd_pread(const Values& params, Values& results, Trap::Ptr* trap) { |
| assert(false); |
| return Result::Ok; |
| } |
| |
| Result fd_readdir(const Values& params, Values& results, Trap::Ptr* trap) { |
| assert(false); |
| return Result::Ok; |
| } |
| |
| Result fd_write(const Values& params, Values& results, Trap::Ptr* trap) { |
| int32_t fd = params[0].Get<u32>(); |
| int32_t iovptr = params[1].Get<u32>(); |
| int32_t iovcnt = params[2].Get<u32>(); |
| __wasi_iovec_t* wasm_iovs; |
| CHECK_RESULT(getMemPtr<__wasi_iovec_t>(iovptr, iovcnt, &wasm_iovs, trap)); |
| std::vector<uvwasi_ciovec_t> iovs(iovcnt); |
| for (int i = 0; i < iovcnt; i++) { |
| iovs[i].buf_len = wasm_iovs[i].buf_len; |
| CHECK_RESULT(getMemPtr<const uint8_t>( |
| wasm_iovs[i].buf, wasm_iovs[i].buf_len, |
| reinterpret_cast<const uint8_t**>(&iovs[i].buf), trap)); |
| } |
| __wasi_ptr_t* out_addr; |
| CHECK_RESULT( |
| getMemPtr<__wasi_ptr_t>(params[3].Get<u32>(), 1, &out_addr, trap)); |
| results[0].Set<u32>( |
| uvwasi_fd_write(uvwasi, fd, iovs.data(), iovs.size(), out_addr)); |
| return Result::Ok; |
| } |
| |
| Result fd_pwrite(const Values& params, Values& results, Trap::Ptr* trap) { |
| assert(false); |
| return Result::Ok; |
| } |
| |
| Result fd_close(const Values& params, Values& results, Trap::Ptr* trap) { |
| assert(false); |
| return Result::Ok; |
| } |
| |
| Result fd_seek(const Values& params, Values& results, Trap::Ptr* trap) { |
| /* __wasi_errno_t __wasi_fd_seek(__wasi_fd_t fd, |
| * __wasi_filedelta_t offset, |
| * __wasi_whence_t whence, |
| * __wasi_filesize_t *newoffset) |
| */ |
| int32_t fd = params[0].Get<u32>(); |
| __wasi_filedelta_t offset = params[1].Get<u32>(); |
| __wasi_whence_t whence = params[2].Get<u32>(); |
| uint32_t newoffset_ptr = params[3].Get<u32>(); |
| uvwasi_filesize_t newoffset; |
| results[0].Set<u32>(uvwasi_fd_seek(uvwasi, fd, offset, whence, &newoffset)); |
| CHECK_RESULT(writeValue<__wasi_filesize_t>(newoffset, newoffset_ptr, trap)); |
| return Result::Ok; |
| } |
| |
| Result environ_get(const Values& params, Values& results, Trap::Ptr* trap) { |
| uvwasi_size_t environc; |
| uvwasi_size_t environ_buf_size; |
| uvwasi_environ_sizes_get(uvwasi, &environc, &environ_buf_size); |
| uint32_t wasm_buf = params[1].Get<u32>(); |
| char* buf; |
| CHECK_RESULT(getMemPtr<char>(wasm_buf, environ_buf_size, &buf, trap)); |
| std::vector<char*> host_env(environc); |
| uvwasi_environ_get(uvwasi, host_env.data(), buf); |
| |
| // Copy host_env pointer array wasm_env) |
| for (uvwasi_size_t i = 0; i < environc; i++) { |
| uint32_t rel_address = host_env[i] - buf; |
| uint32_t dest = params[0].Get<u32>() + (i * sizeof(uint32_t)); |
| CHECK_RESULT(writeValue<uint32_t>(wasm_buf + rel_address, dest, trap)); |
| } |
| |
| results[0].Set<u32>(__WASI_ERRNO_SUCCESS); |
| return Result::Ok; |
| } |
| |
| Result environ_sizes_get(const Values& params, |
| Values& results, |
| Trap::Ptr* trap) { |
| uvwasi_size_t environc; |
| uvwasi_size_t environ_buf_size; |
| uvwasi_environ_sizes_get(uvwasi, &environc, &environ_buf_size); |
| CHECK_RESULT(writeValue<uint32_t>(environc, params[0].Get<u32>(), trap)); |
| CHECK_RESULT( |
| writeValue<uint32_t>(environ_buf_size, params[1].Get<u32>(), trap)); |
| if (trace_stream) { |
| trace_stream->Writef("environ_sizes_get -> %d %d\n", environc, |
| environ_buf_size); |
| } |
| results[0].Set<u32>(__WASI_ERRNO_SUCCESS); |
| return Result::Ok; |
| } |
| |
| Result args_get(const Values& params, Values& results, Trap::Ptr* trap) { |
| uvwasi_size_t argc; |
| uvwasi_size_t arg_buf_size; |
| uvwasi_args_sizes_get(uvwasi, &argc, &arg_buf_size); |
| uint32_t wasm_buf = params[1].Get<u32>(); |
| char* buf; |
| CHECK_RESULT(getMemPtr<char>(wasm_buf, arg_buf_size, &buf, trap)); |
| std::vector<char*> host_args(argc); |
| uvwasi_args_get(uvwasi, host_args.data(), buf); |
| |
| // Copy host_args pointer array wasm_args) |
| for (uvwasi_size_t i = 0; i < argc; i++) { |
| uint32_t rel_address = host_args[i] - buf; |
| uint32_t dest = params[0].Get<u32>() + (i * sizeof(uint32_t)); |
| CHECK_RESULT(writeValue<uint32_t>(wasm_buf + rel_address, dest, trap)); |
| } |
| results[0].Set<u32>(__WASI_ERRNO_SUCCESS); |
| return Result::Ok; |
| } |
| |
| Result args_sizes_get(const Values& params, |
| Values& results, |
| Trap::Ptr* trap) { |
| uvwasi_size_t argc; |
| uvwasi_size_t arg_buf_size; |
| uvwasi_args_sizes_get(uvwasi, &argc, &arg_buf_size); |
| CHECK_RESULT(writeValue<uint32_t>(argc, params[0].Get<u32>(), trap)); |
| CHECK_RESULT( |
| writeValue<uint32_t>(arg_buf_size, params[1].Get<u32>(), trap)); |
| if (trace_stream) { |
| trace_stream->Writef("args_sizes_get -> %d %d\n", argc, arg_buf_size); |
| } |
| results[0].Set<u32>(__WASI_ERRNO_SUCCESS); |
| return Result::Ok; |
| } |
| |
| // The trace stream accosiated with the instance. |
| Stream* trace_stream; |
| |
| Instance::Ptr instance; |
| |
| private: |
| // Write a value into wasm-memory and the given memory offset. |
| template <typename T> |
| Result writeValue(T value, uint32_t target_address, Trap::Ptr* trap) { |
| T* abs_address; |
| CHECK_RESULT(getMemPtr<T>(target_address, sizeof(T), &abs_address, trap)); |
| *abs_address = value; |
| return Result::Ok; |
| } |
| |
| // Result a wasm-memory-local address to an absolute memory location. |
| template <typename T> |
| Result getMemPtr(uint32_t address, |
| uint32_t num_elems, |
| T** result, |
| Trap::Ptr* trap) { |
| if (!memory->IsValidAccess(address, 0, num_elems * sizeof(T))) { |
| *trap = |
| Trap::New(*instance.store(), |
| StringPrintf("out of bounds memory access: " |
| "[%u, %" PRIu64 ") >= max value %" PRIu64, |
| address, u64{address} + num_elems * sizeof(T), |
| memory->ByteSize())); |
| return Result::Error; |
| } |
| if (result) { |
| *result = reinterpret_cast<T*>(memory->UnsafeData() + address); |
| } |
| return Result::Ok; |
| } |
| |
| uvwasi_s* uvwasi; |
| // The memory accociated with the instance. Looked up once on startup |
| // and cached here. |
| Memory* memory; |
| }; |
| |
| std::unordered_map<Instance*, WasiInstance*> wasiInstances; |
| |
| // TODO(sbc): Auto-generate this. |
| |
| #define WASI_CALLBACK(NAME) \ |
| static Result NAME(Thread& thread, const Values& params, Values& results, \ |
| Trap::Ptr* trap) { \ |
| Instance* instance = thread.GetCallerInstance(); \ |
| assert(instance); \ |
| WasiInstance* wasi_instance = wasiInstances[instance]; \ |
| if (wasi_instance->trace_stream) { \ |
| wasi_instance->trace_stream->Writef( \ |
| ">>> running wasi function \"%s\":\n", #NAME); \ |
| } \ |
| return wasi_instance->NAME(params, results, trap); \ |
| } |
| |
| #define WASI_FUNC(NAME) WASI_CALLBACK(NAME) |
| #include "wasi_api.def" |
| #undef WASI_FUNC |
| |
| } // namespace |
| |
| namespace wabt { |
| namespace interp { |
| |
| Result WasiBindImports(const Module::Ptr& module, |
| RefVec& imports, |
| Stream* stream, |
| Stream* trace_stream) { |
| Store* store = module.store(); |
| for (auto&& import : module->desc().imports) { |
| if (import.type.type->kind != ExternKind::Func) { |
| stream->Writef("wasi error: invalid import type: %s\n", |
| import.type.name.c_str()); |
| return Result::Error; |
| } |
| |
| if (import.type.module != "wasi_snapshot_preview1" && |
| import.type.module != "wasi_unstable") { |
| stream->Writef("wasi error: unknown module import: `%s`\n", |
| import.type.module.c_str()); |
| return Result::Error; |
| } |
| |
| auto func_type = *cast<FuncType>(import.type.type.get()); |
| auto import_name = StringPrintf("%s.%s", import.type.module.c_str(), |
| import.type.name.c_str()); |
| HostFunc::Ptr host_func; |
| |
| // TODO(sbc): Validate signatures of imports. |
| #define WASI_FUNC(NAME) \ |
| if (import.type.name == #NAME) { \ |
| host_func = HostFunc::New(*store, func_type, NAME); \ |
| goto found; \ |
| } |
| #include "wasi_api.def" |
| #undef WASI_FUNC |
| |
| stream->Writef("unknown wasi API import: `%s`\n", import.type.name.c_str()); |
| return Result::Error; |
| found: |
| imports.push_back(host_func.ref()); |
| } |
| |
| return Result::Ok; |
| } |
| |
| Result WasiRunStart(const Instance::Ptr& instance, |
| uvwasi_s* uvwasi, |
| Stream* err_stream, |
| Stream* trace_stream) { |
| Store* store = instance.store(); |
| auto module = store->UnsafeGet<Module>(instance->module()); |
| auto&& module_desc = module->desc(); |
| |
| Func::Ptr start; |
| Memory::Ptr memory; |
| for (auto&& export_ : module_desc.exports) { |
| if (export_.type.name == "memory") { |
| if (export_.type.type->kind != ExternalKind::Memory) { |
| err_stream->Writef("wasi error: memory export has incorrect type\n"); |
| return Result::Error; |
| } |
| memory = store->UnsafeGet<Memory>(instance->memories()[export_.index]); |
| } |
| if (export_.type.name == "_start") { |
| if (export_.type.type->kind != ExternalKind::Func) { |
| err_stream->Writef("wasi error: _start export is not a function\n"); |
| return Result::Error; |
| } |
| start = store->UnsafeGet<Func>(instance->funcs()[export_.index]); |
| } |
| if (start && memory) { |
| break; |
| } |
| } |
| |
| if (!start) { |
| err_stream->Writef("wasi error: _start export not found\n"); |
| return Result::Error; |
| } |
| |
| if (!memory) { |
| err_stream->Writef("wasi error: memory export not found\n"); |
| return Result::Error; |
| } |
| |
| if (start->type().params.size() || start->type().results.size()) { |
| err_stream->Writef("wasi error: invalid _start signature\n"); |
| return Result::Error; |
| } |
| |
| // Register memory |
| WasiInstance wasi(instance, uvwasi, memory.get(), trace_stream); |
| wasiInstances[instance.get()] = &wasi; |
| |
| // Call start ([] -> []) |
| Values params; |
| Values results; |
| Trap::Ptr trap; |
| Result res = start->Call(*store, params, results, &trap, trace_stream); |
| if (trap) { |
| WriteTrap(err_stream, "error", trap); |
| } |
| |
| // Unregister memory |
| wasiInstances.erase(instance.get()); |
| return res; |
| } |
| |
| } // namespace interp |
| } // namespace wabt |
| |
| #endif |