Skip to content

Commit

Permalink
Implement directory mapping and WASI file functions
Browse files Browse the repository at this point in the history
Implement path_open, fd_seek, fd_read, environ_sizes_get, environ_get
for file acces.
Implement the mapping of real directories to virtual ones so that WASI
can use different directories.
Add flag '--mapdirs <real> <virtual>' for mapping directories.
Add flag '--env' for sharing host envrionment variables.
Add flag '--help' for printing available walrus options.
Also improve random_get test to not have a result.
Change test runner to run wasi tests correctly.

Signed-off-by: Adam Laszlo Kulcsar <kuladam@inf.u-szeged.hu>
  • Loading branch information
kulcsaradam committed Jan 10, 2024
1 parent 52ee28c commit 87359e0
Show file tree
Hide file tree
Showing 20 changed files with 448 additions and 74 deletions.
29 changes: 29 additions & 0 deletions src/runtime/SpecTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class SpecTestFunctionTypes {
I32_RI32,
I32I32_RI32,
I32I32I32I32_RI32,
I32I64I32I32_RI32,
I32I32I32I32I32I64I64I32I32_RI32,
RI32,
I64R,
F32R,
Expand Down Expand Up @@ -88,6 +90,33 @@ class SpecTestFunctionTypes {
result->push_back(Value::Type::I32);
m_vector[index++] = new FunctionType(param, result);
}
{
// I32I64I32I32_RI32
param = new ValueTypeVector();
result = new ValueTypeVector();
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I64);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
result->push_back(Value::Type::I32);
m_vector[index++] = new FunctionType(param, result);
}
{
// I32I32I32I32I32I64I64I32I32_RI32
param = new ValueTypeVector();
result = new ValueTypeVector();
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I64);
param->push_back(Value::Type::I64);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
result->push_back(Value::Type::I32);
m_vector[index++] = new FunctionType(param, result);
}
{
// RI32
param = new ValueTypeVector();
Expand Down
49 changes: 44 additions & 5 deletions src/shell/Shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "wabt/wast-lexer.h"
#include "wabt/wast-parser.h"
#include "wabt/binary-writer.h"
#include "string-view-lite/string_view.h"

struct spectestseps : std::numpunct<char> {
char do_thousands_sep() const { return '_'; }
Expand All @@ -39,6 +40,7 @@ struct ArgParser {

static bool useJIT = false;
static int jitVerbose = 0;
static bool shareHostEnv = false;

using namespace Walrus;

Expand Down Expand Up @@ -999,11 +1001,12 @@ static void runExports(Store* store, const std::string& filename, const std::vec
&data);
}

static void parseArguments(int argc, char* argv[], ArgParser& argParser)
static void parseArguments(int argc, char* argv[], ArgParser& argParser, std::vector<uvwasi_preopen_s>& preopenDirs)
{
const std::vector<nonstd::string_view> args(argv + 1, argv + argc);

for (auto it = args.begin(); it != args.end(); ++it) {
auto it = args.begin();
while (it != args.end()) {
if (*it == "--run-export") {
if (*it == args.back()) {
fprintf(stderr, "error: --run-export requires an argument\n");
Expand All @@ -1018,6 +1021,40 @@ static void parseArguments(int argc, char* argv[], ArgParser& argParser)
jitVerbose = 1;
} else if (*it == "--jit-verbose-color") {
jitVerbose = 2;
} else if (*it == "--mapdirs") {
it++;
while (it != args.end() && nonstd::to_string(*it).find("--") == std::string::npos) {
if (it + 1 == args.end()) {
break;
}

uvwasi_preopen_s preopen;
preopen.real_path = reinterpret_cast<char*>(calloc(1, it->length() + 1));
it->nonstd::sv_lite::string_view::copy(const_cast<char*>(preopen.real_path), it->length());

it++;

preopen.mapped_path = reinterpret_cast<char*>(calloc(1, it->length() + 1));
it->nonstd::sv_lite::string_view::copy(const_cast<char*>(preopen.mapped_path), it->length());
preopenDirs.push_back(preopen);
it++;
}
it--;
} else if (*it == "--env") {
shareHostEnv = true;
} else if (*it == "--help" || *it == "-h") {
fprintf(stdout, "Run a WebAssembly wasm, wat or wast file.\n\n");
fprintf(stdout, "Usage: walrus [OPTIONS] <INPUT>\n\n");
fprintf(stdout, "OPTIONS:\n");
fprintf(stdout, "\t-h, --help\n\t\tShow this message then exit.\n\n");
fprintf(stdout, "\t--jit\n\t\tEnable just-in-time interpretation.\n\n");
fprintf(stdout, "\t--jit-verbose\n\t\tEnable verbose output for just-in-time interpretation.\n\n");
fprintf(stdout, "\t--jit-verbose-color\n\t\tEnable colored verbose output for just-in-time interpretation.\n\n");
fprintf(stdout, "\t--mapdirs <HOST_DIR> <VIRTUAL_DIR>\n\t\tMap real directories to virtual ones for WASI functions to use.\n\t\tExample: ./walrus test.wasm --mapdirs this/real/directory/ this/virtual/directory\n\n");
fprintf(stdout, "\t--env\n\t\tShare host environment to walrus WASI.\n\n");


exit(0);
} else {
auto arg = nonstd::to_string(*it);
if (endsWith(arg, "wat") || endsWith(arg, "wast") || endsWith(arg, "wasm")) {
Expand All @@ -1027,6 +1064,7 @@ static void parseArguments(int argc, char* argv[], ArgParser& argParser)
exit(1);
}
}
++it;
}

if (argParser.fileNames.empty()) {
Expand All @@ -1035,7 +1073,7 @@ static void parseArguments(int argc, char* argv[], ArgParser& argParser)
}
}

int main(int argc, char* argv[])
int main(int argc, char* argv[], char* envp[])
{
#ifndef NDEBUG
setbuf(stdout, NULL);
Expand All @@ -1055,12 +1093,13 @@ int main(int argc, char* argv[])

Engine* engine = new Engine();
Store* store = new Store(engine);
WASI* wasi = new WASI();

std::vector<uvwasi_preopen_s> preopenDirs;
SpecTestFunctionTypes functionTypes;
ArgParser argParser;

parseArguments(argc, argv, argParser);
parseArguments(argc, argv, argParser, preopenDirs);
WASI* wasi = new WASI(preopenDirs, shareHostEnv, envp);

for (const auto& filePath : argParser.fileNames) {
FILE* fp = fopen(filePath.data(), "r");
Expand Down
41 changes: 41 additions & 0 deletions src/wasi/Environ.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2023-present Samsung Electronics Co., Ltd
*
* 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.
*/

namespace Walrus {

void WASI::environ_sizes_get(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t count = argv[0].asI32();
uint32_t buf = argv[1].asI32();

uvwasi_size_t* uvCount = reinterpret_cast<uvwasi_size_t*>(instance->memory(0)->buffer() + count);
uvwasi_size_t* uvBufSize = reinterpret_cast<uvwasi_size_t*>(instance->memory(0)->buffer() + buf);

result[0] = Value(static_cast<uint16_t>(uvwasi_environ_sizes_get(WASI::m_uvwasi, uvCount, uvBufSize)));
}

void WASI::environ_get(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t env = argv[0].asI32();
uint32_t environBuf = argv[1].asI32();

char** uvEnviron = reinterpret_cast<char**>(instance->memory(0)->buffer() + env);
char* uvEnvironBuf = reinterpret_cast<char*>(instance->memory(0)->buffer() + environBuf);

result[0] = Value(static_cast<uint16_t>(uvwasi_environ_get(WASI::m_uvwasi, uvEnviron, uvEnvironBuf)));
}

} // namespace Walrus
41 changes: 40 additions & 1 deletion src/wasi/Fd.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void WASI::fd_write(ExecutionState& state, Value* argv, Value* result, Instance*
std::vector<uvwasi_ciovec_t> iovs(iovcnt);
for (uint32_t i = 0; i < iovcnt; i++) {
iovs[i].buf = instance->memory(0)->buffer() + *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + i * 8);
iovs[i].buf_len = *(instance->memory(0)->buffer() + iovptr + 4 + i * 8);
iovs[i].buf_len = *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + 4 + i * 8);
}

uvwasi_size_t out_addr = *(instance->memory(0)->buffer() + out);
Expand All @@ -40,4 +40,43 @@ void WASI::fd_write(ExecutionState& state, Value* argv, Value* result, Instance*
*(instance->memory(0)->buffer() + out) = out_addr;
}

void WASI::fd_read(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t fd = argv[0].asI32();
uint32_t iovptr = argv[1].asI32();
uint32_t iovcnt = argv[2].asI32();
uint32_t out = argv[3].asI32();

std::vector<uvwasi_iovec_t> iovs(iovcnt);
for (uint32_t i = 0; i < iovcnt; i++) {
iovs[i].buf = instance->memory(0)->buffer() + *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + i * 8);
iovs[i].buf_len = *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + 4 + i * 8);
}

uvwasi_size_t out_addr = *(instance->memory(0)->buffer() + out);

result[0] = Value(static_cast<int16_t>(uvwasi_fd_read(WASI::m_uvwasi, fd, iovs.data(), iovs.size(), &out_addr)));
*(instance->memory(0)->buffer() + out) = out_addr;
}

void WASI::fd_close(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t fd = argv[0].asI32();

result[0] = Value(static_cast<int16_t>(uvwasi_fd_close(WASI::m_uvwasi, fd)));
}

void WASI::fd_seek(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t fd = argv[0].asI32();
uint64_t fileDelta = argv[1].asI32();
uint32_t whence = argv[2].asI32();
uint64_t newOffset = argv[3].asI32();

uvwasi_filesize_t out_addr = *(instance->memory(0)->buffer() + newOffset);

result[0] = Value(static_cast<int16_t>(uvwasi_fd_seek(WASI::m_uvwasi, fd, fileDelta, whence, &out_addr)));
*(instance->memory(0)->buffer() + newOffset) = out_addr;
}

} // namespace Walrus
48 changes: 48 additions & 0 deletions src/wasi/Path.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2023-present Samsung Electronics Co., Ltd
*
* 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.
*/

namespace Walrus {

void WASI::path_open(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t fd = argv[0].asI32();
uint32_t dirflags = argv[1].asI32();
uint32_t path_offset = argv[2].asI32();
uint32_t len = argv[3].asI32();
uint32_t oflags = argv[4].asI32();
uint64_t rights = argv[5].asI64();
uint64_t right_inheriting = argv[6].asI64();
uint32_t fdflags = argv[7].asI32();
uint32_t ret_fd_offset = argv[8].asI32();

uvwasi_fd_t* ret_fd = reinterpret_cast<uvwasi_fd_t*>(instance->memory(0)->buffer() + ret_fd_offset);

const char* path = reinterpret_cast<char*>(instance->memory(0)->buffer() + path_offset);

result[0] = Value(static_cast<uint16_t>(
uvwasi_path_open(WASI::m_uvwasi,
fd,
dirflags,
path,
len,
oflags,
rights,
right_inheriting,
fdflags,
ret_fd)));
}

} // namespace Walrus
15 changes: 12 additions & 3 deletions src/wasi/Wasi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include "wasi/Wasi.h"
#include "wasi/Fd.h"
#include "wasi/Path.h"
#include "wasi/Environ.h"

// https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md

Expand Down Expand Up @@ -87,11 +89,10 @@ void WASI::fillWasiFuncTable()
#undef WASI_FUNC_TABLE
}

WASI::WASI()
WASI::WASI(std::vector<uvwasi_preopen_s>& preopenDirs, bool shareHostEnv, char** envp)
{
fillWasiFuncTable();

uvwasi_t uvwasi;
WASI::m_uvwasi = reinterpret_cast<uvwasi_t*>(malloc(sizeof(uvwasi_t)));

uvwasi_options_t init_options;
Expand All @@ -102,7 +103,15 @@ WASI::WASI()
init_options.argc = 0;
init_options.argv = nullptr;
init_options.envp = nullptr;
init_options.preopenc = 0;
if (shareHostEnv) {
init_options.envp = const_cast<const char**>(envp);
}
init_options.preopenc = preopenDirs.size();
init_options.preopens = reinterpret_cast<uvwasi_preopen_t*>(calloc(init_options.preopenc, sizeof(uvwasi_preopen_s)));
for (unsigned i = 0; i < preopenDirs.size(); i++) {
init_options.preopens[i].mapped_path = preopenDirs[i].mapped_path;
init_options.preopens[i].real_path = preopenDirs[i].real_path;
}
init_options.preopen_socketc = 0;
init_options.allocator = nullptr;

Expand Down
23 changes: 18 additions & 5 deletions src/wasi/Wasi.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,12 @@ class WASI {
#undef TO_ENUM
// end of type definitions

WASI();
WASI(std::vector<uvwasi_preopen_s>& preopenDirs, bool shareHostEnv, char**);

~WASI()
{
::uvwasi_destroy(m_uvwasi);
free(m_uvwasi);
}

struct WasiFunc {
Expand All @@ -129,10 +130,16 @@ class WASI {
WasiFunction::WasiFunctionCallback ptr;
};

#define FOR_EACH_WASI_FUNC(F) \
F(proc_exit, I32R) \
F(random_get, I32I32_RI32) \
F(fd_write, I32I32I32I32_RI32)
#define FOR_EACH_WASI_FUNC(F) \
F(proc_exit, I32R) \
F(random_get, I32I32_RI32) \
F(fd_write, I32I32I32I32_RI32) \
F(fd_read, I32I32I32I32_RI32) \
F(fd_close, I32_RI32) \
F(fd_seek, I32I64I32I32_RI32) \
F(path_open, I32I32I32I32I32I64I64I32I32_RI32) \
F(environ_get, I32I32_RI32) \
F(environ_sizes_get, I32I32_RI32)

enum WasiFuncName : size_t {
#define DECLARE_FUNCTION(NAME, FUNCTYPE) NAME##FUNC,
Expand All @@ -148,6 +155,12 @@ class WASI {

static void proc_exit(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void fd_write(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void fd_read(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void fd_close(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void fd_seek(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void path_open(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void environ_get(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void environ_sizes_get(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void random_get(ExecutionState& state, Value* argv, Value* result, Instance* instance);

static WasiFunc m_wasiFunctions[FuncEnd];
Expand Down
Loading

0 comments on commit 87359e0

Please sign in to comment.